Ini memang bisa dilakukan dalam waktu linier, O (n) , dan O (n) ruang ekstra. Saya akan menganggap array input adalah string karakter, tetapi ini tidak penting.
Sebuah metode naif akan - setelah pencocokan k karakter yang sama - menemukan karakter yang tidak cocok, dan kembali k-1 unit di sebuah , ulang indeks di b , dan kemudian memulai proses pencocokan dari sana. Ini jelas merupakan kasus terburuk O (n²) .
Untuk menghindari proses pengulangan ini, kita dapat mengamati bahwa kembali tidak berguna jika kita belum menemukan karakter b [0] saat memindai karakter k-1 terakhir . Jika kita melakukan menemukan karakter itu, maka mundur ke posisi itu hanya akan berguna, jika dalam k berukuran substring kami memiliki pengulangan periodik.
Misalnya, jika kita melihat substring "abcabc" di suatu tempat di a , dan b adalah "abcabd", dan kami menemukan bahwa karakter terakhir dari b tidak cocok, kita harus mempertimbangkan bahwa pertandingan yang berhasil mungkin dimulai pada "a" yang kedua. di substring, dan kita harus memindahkan indeks saat ini di b kembali sesuai sebelum melanjutkan perbandingan.
Idenya adalah untuk melakukan beberapa preprocessing berdasarkan string b untuk login kembali-referensi dalam b yang berguna untuk memeriksa ketika ada ketidakcocokan. Jadi misalnya, jika b adalah "acaacaacd", kita dapat mengidentifikasi referensi-ulang berbasis-0 ini (letakkan di bawah setiap karakter):
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
Misalnya, jika kita memiliki yang sama dengan "acaacaaca" ketidakcocokan pertama terjadi pada karakter terakhir. Informasi di atas kemudian memberi tahu algoritma untuk kembali dalam b ke indeks 5, karena "acaac" adalah umum. Dan kemudian dengan hanya mengubah indeks saat ini di b kita dapat melanjutkan pencocokan pada indeks saat ini dari a . Dalam contoh ini, pencocokan karakter akhir kemudian berhasil.
Dengan ini kita dapat mengoptimalkan pencarian dan memastikan bahwa indeks dalam sebuah selalu bisa maju ke depan.
Berikut ini adalah implementasi dari gagasan itu dalam JavaScript, menggunakan sintaksis paling dasar dari bahasa itu saja:
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
Meskipun ada while
loop bersarang , ini tidak memiliki lebih banyak iterasi total daripada n . Ini karena nilai k secara ketat menurun dalam while
tubuh, dan tidak bisa menjadi negatif. Ini hanya bisa terjadi ketika k++
dieksekusi yang berkali-kali memberi ruang yang cukup untuk penurunan tersebut. Jadi semuanya, tidak mungkin ada lebih banyak eksekusi while
tubuh daripada k++
eksekusi, dan yang terakhir jelas O (n).
Untuk menyelesaikan, di sini Anda dapat menemukan kode yang sama seperti di atas, tetapi dalam cuplikan interaktif: Anda dapat memasukkan string Anda sendiri dan melihat hasilnya secara interaktif:
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]
dan kemudian pergi ke arraya
menghitung hash untuka[1] to a[d]
jika itu cocok maka itu jawaban Anda, jika tidak menghitung hash untuka[2] to a[d+1]
dengan menggunakan kembali hash dihitung untuka[1] to a[d]
. Tapi saya tidak tahu apakah objek dalam array dapat menerima hash bergulir untuk dihitung pada mereka.