【luogu P5410】【模板】扩展 KMP(Z 函数)(字符串)

【模板】扩展 KMP(Z 函数)

题目链接:luogu P5410

题目大意

给你一个字符串,要你求它每个后缀跟它 LCP 的长度。(Z 函数)
给你两个字符串,要你求一个字符串每个后缀跟另一个字符串 LCP 的长度。(exKMP)

思路

这个东西感觉有点马拉车的感觉。

考虑知道了当前的答案 \(z_{1\sim i-1}\),考虑怎么求 \(z_i\)
首先我们肯定是找到一个可以匹配的串 \([l,r]\),那肯定是找 \(r\) 最大的,记录当前是 \(l,r\)

然后如果 \(i\leqslant r\),我们考虑利用它,通过证明可以得到 \(r_i\geqslant \min(r-i+1,r_{i-l+1})\)
证明考虑逐位证,设 \(1\leqslant x\leqslant \min(r-i+1,r_{i-l+1})\)
\(s_{i+x-1}=s_{l+(i+x-l)-1}\)
\(=s_{1+(i+x-l)-1}=s_{i+x-l}\)(左边的限制条件)
\(=s_{1+(i-l+1)+x}\)
\(=s_{1+x}\)(当 \(x\leq z_{i-l+1}\)

然后我们再暴力扩展,然后维护最大 \(r\) 对于的 \(l,r\)
然后每个数只会别暴力扩展一次,所以复杂度是 \(O(n)\)

至于两个字符之间的 exKMP,我们就一样的匹配法。

代码

#include<cstdio>
#include<cstring>
#include<iostream>
#define ll long long

using namespace std;

const int N = 2e7 + 5;
char a[N], b[N];
int an, bn, z[N], p[N];
ll ans;

void get_Z(char *s, int n) {
	for (int i = 1; i <= n; i++) z[i] = 0;
	z[1] = n;
	for (int i = 2, l = 0, r = 0; i <= n; i++) {
		if (i <= r) z[i] = min(z[i - l + 1], r - i + 1);
		while (i + z[i] <= n && s[i + z[i]] == s[1 + z[i]]) z[i]++;
		if (i + z[i] - 1 > r) l = i, r = i + z[i] - 1;
	}
}

void exKMP(char *a, int n, char *b, int m) {
	for (int i = 1; i <= n; i++) p[i] = 0;
	for (int i = 1, l = 0, r = 0; i <= n; i++) {
		if (i <= r) p[i] = min(z[i - l + 1], r - i + 1);
		while (i + p[i] <= n && a[i + p[i]] == b[1 + p[i]]) p[i]++;
		if (i + p[i] - 1 > r) l = i, r = i + p[i] - 1;
	}
}

int main() {
	scanf("%s", a + 1); scanf("%s", b + 1);
	an = strlen(a + 1); bn = strlen(b + 1);
	
	get_Z(b, bn);
	ans = 0; for (int i = 1; i <= bn; i++) ans ^= 1ll * i * (z[i] + 1); printf("%lld\n", ans);
	exKMP(a, an, b, bn);
	ans = 0; for (int i = 1; i <= an; i++) ans ^= 1ll * i * (p[i] + 1); printf("%lld", ans);
	
	return 0;
}
posted @ 2022-07-22 10:44  あおいSakura  阅读(25)  评论(0编辑  收藏  举报