Điều này thực sự có thể được thực hiện trong thời gian tuyến tính, không gian thêm O (n) và O (n) . Tôi sẽ giả sử các mảng đầu vào là các chuỗi ký tự, nhưng điều này không cần thiết.
Một phương thức ngây thơ sẽ - sau khi khớp các ký tự k bằng nhau - tìm một ký tự không khớp và quay lại các đơn vị k-1 trong a , đặt lại chỉ mục trong b , rồi bắt đầu quá trình khớp từ đó. Điều này rõ ràng đại diện cho một trường hợp xấu nhất O (n²) .
Để tránh quá trình quay lui này, chúng ta có thể quan sát rằng việc quay lại không hữu ích nếu chúng ta không gặp phải ký tự b [0] trong khi quét các ký tự k-1 cuối cùng . Nếu chúng ta đã tìm thấy ký tự đó, thì việc quay lại vị trí đó sẽ chỉ hữu ích, nếu trong chuỗi con có kích thước k đó, chúng ta có sự lặp lại định kỳ.
Chẳng hạn, nếu chúng ta xem chuỗi con "abcabc" ở đâu đó trong a và b là "abcabd" và chúng ta thấy rằng ký tự cuối cùng của b không khớp, chúng ta phải xem xét rằng một trận đấu thành công có thể bắt đầu từ "a" thứ hai trong chuỗi con và chúng ta nên di chuyển chỉ số hiện tại của mình trở lại b trước khi tiếp tục so sánh.
Ý tưởng là sau đó thực hiện một số tiền xử lý dựa trên chuỗi b để ghi lại các tham chiếu ngược trong b rất hữu ích để kiểm tra khi có sự không phù hợp. Vì vậy, ví dụ, nếu b là "acaacaacd", chúng ta có thể xác định các phản hồi dựa trên 0 này (đặt bên dưới mỗi ký tự):
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
Ví dụ: nếu chúng ta có một "acaacaaca" thì sự không khớp đầu tiên xảy ra với ký tự cuối cùng. Các thông tin trên sau đó cho biết thuật toán quay trở lại b đến chỉ số 5, vì "acaac" là phổ biến. Và sau đó, chỉ với việc thay đổi chỉ mục hiện tại trong b, chúng ta có thể tiếp tục khớp ở chỉ số hiện tại của a . Trong ví dụ này, trận đấu của nhân vật cuối cùng sẽ thành công.
Với điều này, chúng tôi có thể tối ưu hóa tìm kiếm và đảm bảo rằng chỉ mục trong một luôn có thể tiến lên phía trước.
Đây là cách triển khai ý tưởng đó trong JavaScript, chỉ sử dụng cú pháp cơ bản nhất của ngôn ngữ đó:
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
Mặc dù có các while
vòng lặp lồng nhau , chúng không có tổng số lần lặp nhiều hơn n . Điều này là do giá trị của k giảm nghiêm trọng trong while
cơ thể và không thể trở thành âm. Điều này chỉ có thể xảy ra khi k++
được thực hiện nhiều lần để có đủ chỗ cho những lần giảm như vậy. Vì vậy, tất cả trong tất cả, không thể có nhiều vụ hành quyết của while
cơ thể hơn là các k++
vụ hành quyết, và sau đó rõ ràng là O (n).
Để hoàn thành, ở đây bạn có thể tìm thấy mã giống như trên, nhưng trong một đoạn tương tác: bạn có thể nhập chuỗi của riêng mình và xem kết quả tương tác:
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]
và sau đó chuyển sanga
tính toán băm mảnga[1] to a[d]
nếu phù hợp với câu trả lời của bạn, nếu không tính toán hàm băma[2] to a[d+1]
bằng cách sử dụng lại hàm băm được tính toána[1] to a[d]
. Nhưng tôi không biết liệu các đối tượng trong mảng có thể chấp nhận được một hàm băm lăn được tính toán trên chúng hay không.