यह वास्तव में रैखिक समय, O (n) , और O (n) अतिरिक्त स्थान में किया जा सकता है। मुझे लगता है कि इनपुट सरणियां चरित्र के तार हैं, लेकिन यह आवश्यक नहीं है।
एक अनुभवहीन विधि होगा - मिलान के बाद कश्मीर अक्षर हैं जो बराबर हैं - एक चरित्र से मेल नहीं खाता, और वापस जाने के लिए कश्मीर -1 में इकाइयों एक , सूचकांक में रीसेट ख , और फिर वहाँ से मिलान प्रक्रिया शुरू करते हैं। यह स्पष्ट रूप से O (n²) सबसे खराब स्थिति का प्रतिनिधित्व करता है ।
इस बैकट्रैकिंग प्रक्रिया से बचने के लिए, हम यह देख सकते हैं कि यदि पिछले k-1 वर्णों को स्कैन करते समय हमने b [0] वर्ण का सामना नहीं किया है तो वापस जाना उपयोगी नहीं है । अगर हम किया था कि चरित्र लगता है, तो उस स्थिति को उलटे पांव लौटने से केवल उपयोगी है, अगर है कि में होगा कश्मीर सबस्ट्रिंग आकार हम एक आवधिक पुनरावृत्ति था।
उदाहरण के लिए, अगर हम "abcabc" में कहीं सबस्ट्रिंग को देखने के लिए एक , और ख "abcabd" है, और हम पाते हैं कि के अंतिम चरित्र ख से मेल नहीं खाता है, हम पर विचार करना चाहिए कि एक सफल मैच दूसरा "एक" में शुरू हो सकता है विकल्प में, और हमें तुलना जारी रखने से पहले अपने वर्तमान सूचकांक को बी बैक में स्थानांतरित करना चाहिए ।
फिर विचार यह है कि बी -बैक-रेफरेंस लॉग करने के लिए स्ट्रिंग बी पर आधारित कुछ प्रीप्रोसेसिंग करें जो कि बेमेल होने पर जांचने के लिए उपयोगी हों। उदाहरण के लिए, यदि b "acaacaacd" है, तो हम इन 0-आधारित बैकरेफ़र (प्रत्येक वर्ण के नीचे रख) की पहचान कर सकते हैं:
index: 0 1 2 3 4 5 6 7 8
b: a c a a c a a c d
ref: 0 0 0 1 0 0 1 0 5
उदाहरण के लिए, यदि हमारे पास एक के बराबर "acaacaaca" पहले बेमेल अंतिम चरित्र पर होता है। उपरोक्त जानकारी तब एल्गोरिथ्म को b से इंडेक्स 5 में वापस जाने के लिए कहती है , क्योंकि "acaac" आम है। और उसके बाद ही में मौजूदा सूचकांक में बदलाव के साथ ख हम की वर्तमान सूचकांक में मिलान जारी रख सकते हैं एक । इस उदाहरण में अंतिम चरित्र का मैच तब सफल होता है।
इसके साथ ही हम खोज अनुकूलन और यह सुनिश्चित करें कि में सूचकांक कर सकते हैं एक हमेशा आगे प्रगति कर सकते हैं।
यहाँ जावास्क्रिप्ट में उस विचार का एक कार्यान्वयन है, केवल उस भाषा के सबसे बुनियादी वाक्यविन्यास का उपयोग करना:
function overlapCount(a, b) {
// Deal with cases where the strings differ in length
let startA = 0;
if (a.length > b.length) startA = a.length - b.length;
let endB = b.length;
if (a.length < b.length) endB = a.length;
// Create a back-reference for each index
// that should be followed in case of a mismatch.
// We only need B to make these references:
let map = Array(endB);
let k = 0; // Index that lags behind j
map[0] = 0;
for (let j = 1; j < endB; j++) {
if (b[j] == b[k]) {
map[j] = map[k]; // skip over the same character (optional optimisation)
} else {
map[j] = k;
}
while (k > 0 && b[j] != b[k]) k = map[k];
if (b[j] == b[k]) k++;
}
// Phase 2: use these references while iterating over A
k = 0;
for (let i = startA; i < a.length; i++) {
while (k > 0 && a[i] != b[k]) k = map[k];
if (a[i] == b[k]) k++;
}
return k;
}
console.log(overlapCount("ababaaaabaabab", "abaababaaz")); // 7
हालांकि नेस्टेड while
लूप हैं, इनमें n की तुलना में कुल पुनरावृत्तियां नहीं हैं । ऐसा इसलिए है क्योंकि शरीर में कश्मीर का मान सख्ती से घटता है while
, और नकारात्मक नहीं हो सकता। यह केवल तब हो सकता है जब k++
निष्पादित किया गया था कि कई बार ऐसे घटने के लिए पर्याप्त जगह देने के लिए। इसलिए सभी में, while
शरीर के अधिक निष्पादन नहीं हो सकते हैं, जहां निष्पादन होते हैं k++
, और उत्तरार्द्ध स्पष्ट रूप से ओ (एन) है।
पूर्ण करने के लिए, आप ऊपर जैसा कोड पा सकते हैं, लेकिन एक इंटरैक्टिव स्निपेट में: आप अपने स्वयं के तार इनपुट कर सकते हैं और परिणाम को अंतःक्रियात्मक रूप से देख सकते हैं:
function overlapCount(a, b) {
// Deal with cases where the strings differ in length
let startA = 0;
if (a.length > b.length) startA = a.length - b.length;
let endB = b.length;
if (a.length < b.length) endB = a.length;
// Create a back-reference for each index
// that should be followed in case of a mismatch.
// We only need B to make these references:
let map = Array(endB);
let k = 0; // Index that lags behind j
map[0] = 0;
for (let j = 1; j < endB; j++) {
if (b[j] == b[k]) {
map[j] = map[k]; // skip over the same character (optional optimisation)
} else {
map[j] = k;
}
while (k > 0 && b[j] != b[k]) k = map[k];
if (b[j] == b[k]) k++;
}
// Phase 2: use these references while iterating over A
k = 0;
for (let i = startA; i < a.length; i++) {
while (k > 0 && a[i] != b[k]) k = map[k];
if (a[i] == b[k]) k++;
}
return k;
}
// I/O handling
let [inputA, inputB] = document.querySelectorAll("input");
let output = document.querySelector("pre");
function refresh() {
let a = inputA.value;
let b = inputB.value;
let count = overlapCount(a, b);
let padding = a.length - count;
// Apply some HTML formatting to highlight the overlap:
if (count) {
a = a.slice(0, -count) + "<b>" + a.slice(-count) + "</b>";
b = "<b>" + b.slice(0, count) + "</b>" + b.slice(count);
}
output.innerHTML = count + " overlapping characters:\n" +
a + "\n" +
" ".repeat(padding) + b;
}
document.addEventListener("input", refresh);
refresh();
body { font-family: monospace }
b { background:yellow }
input { width: 90% }
a: <input value="acacaacaa"><br>
b: <input value="acaacaacd"><br>
<pre></pre>
b[1] to b[d]
और फिर उस मैच केa
लिए सरणी कंप्यूट हैश पर जाएं,a[1] to a[d]
तो यह आपका उत्तर है, यदि हैश के लिए गणनाa[2] to a[d+1]
करके हैश का उपयोग करके गणना नहीं हैa[1] to a[d]
। लेकिन मुझे नहीं पता कि अगर सरणी में वस्तुओं को एक रोलिंग हैश के लिए उन पर गणना की जा सकने योग्य है।