【模板】扩展 kmp (exkmp) / Z 函数

发布时间 2023-10-20 10:13:04作者: caijianhong

求出一个字符串 \(s\) 的每个后缀与原串的 LCP。

首先由显然的 SAM 做法。考虑线性。

考虑维护区间 \([l,r]\) 表示 \([l,r]=[1,r-l+1]\) 是最右的匹配段。考虑新的 \(i\),如果满足 \(l\leq i\leq r\),则 \(i\) 可以直接取 \(i-l+1\) 的答案继续扩展,否则继续扩展。最后更新区间。

点击查看代码
#include <cstdio>
#include <vector>
#include <cstring>
#include <cassert>
#include <algorithm>
using namespace std;
#ifdef LOCAL
#define debug(...) fprintf(stderr,##__VA_ARGS__)
#else
#define debug(...) void(0)
#endif
typedef long long LL;
int n, m, z[1 << 26];
char a[1 << 26];
void exkmp(int len) {
	z[1] = len;
	debug("a = %s\n", a + 1);
	for (int i = 2, l = 0, r = 0; i <= len; i++) {
		if (i <= r) z[i] = min(z[i - l + 1], r - i + 1);
		while (i + z[i] <= len && a[1 + z[i]] == a[i + z[i]]) ++z[i];
		if (i + z[i] - 1 > r) r = i + z[l = i] - 1;
		debug("z[%d] = %d\n", i, z[i]);
	}
}
char buf[1 << 26];
int main(){
	scanf("%s%s", buf + 1, a + 1);
	n = strlen(a + 1), m = strlen(buf + 1);
	a[n + 1] = '?';
	for (int i = 1; i <= m; i++) a[n + i + 1] = buf[i];
	exkmp(n + m + 1);
	LL sum1 = n + 1, sum2 = 0;
	for (int i = 2; i <= n; i++) sum1 ^= 1ll * i * (z[i] + 1);
	for (int i = 1; i <= m; i++) sum2 ^= 1ll * i * (z[i + n + 1] + 1);
	printf("%lld\n", sum1);
	printf("%lld\n", sum2);
	return 0;
}