擴展KMP(Z函數)


給定一個串 \(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是判斷一個前綴是否循環以及最小循環節)
  • 解決一些中間子串與前綴的匹配問題。

例題

Password

Prefixes and Suffixes

字符串匹配

模板(調試用)

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;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM