擴充套件KMP(Z函式)
阿新 • • 發佈:2020-12-12
給定一個串 \(A\) 和一個串 \(B\)。問 \(B\) 的所有後綴和 \(A\) 的 \(lcp\)。
\(1 \le |A|,|B| \le 10^7\)
首先考慮解決一個簡單一點的問題:當 \(A=B\) 的時候的答案。
與 KMP 類似,我們需要求一個數組 \(nxt(i)\),表示 \((A...|A|)\) 與 \(A\) 的 \(lcp\)。
與 manacher 類似,我們考慮儘可能地利用之前求出的資訊。
我們維護最靠右的匹配段 \(l...r\),那麼 \(i...|A|\) 和 \(A\) 的匹配的前半部分就相當於 \(1...i-l+1\) 和 \(A\) 的匹配。如果成功匹配上了,那麼還可以繼續匹配。
模擬實現即可。勢能分析可得複雜度為 \(O(|A|)\)。
現在我們有了 \(A\) 的 \(nxt\) 陣列,我們嘗試解決最開始那個問題。這回我們掃 \(B\),同樣維護一個最靠右的匹配段 \(l...r\),那麼 \(i...|B|\) 和 \(A\) 的匹配的前半部分就相當於 \(A\) 的 \(1...i-l+1\) 和 \(A\) 的匹配。如果成功匹配上了,那麼還可以繼續匹配。
複雜度:\(O(|A| + |B|)\)
常見應用
- 找一個字首最多在前面連續迴圈出現多少次。(KMP是判斷一個字首是否迴圈以及最小迴圈節)
- 解決一些中間子串與字首的匹配問題。
例題
模板(除錯用)
next[1] = n; for (int i = 2, l = 1, r = 1; i <= n; ++i) { int tar = i - l + 1, mx = max(0, r - i + 1); next[i] = min(next[tar], mx); while (i + next[i] <= n && s[i + next[i]] == s[next[i] + 1]) ++next[i]; if (i + next[i] - 1 > r) r = i + next[i] - 1, l = i; } int m = strlen(t + 1); for (int i = 1, l = 0, r = 0; i <= m; ++i) { int tar = i - l + 1, mx = max(0, r - i + 1);//bug ans[i] = min(next[tar], mx); while (i + ans[i] <= m && t[i + ans[i]] == s[ans[i] + 1]) ++ans[i]; if (i + ans[i] - 1 > r) r = i + ans[i] - 1, l = i; }