扩展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;
}
posted @ 2020-12-11 20:23  JiaZP  阅读(593)  评论(0编辑  收藏  举报