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

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

题目链接:luogu P5410

题目大意

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

思路

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

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

然后如果 i ⩽ r i\leqslant r ir,我们考虑利用它,通过证明可以得到 r i ⩾ min ⁡ ( r − i + 1 , r i − l + 1 ) r_i\geqslant \min(r-i+1,r_{i-l+1}) rimin(ri+1,ril+1)
证明考虑逐位证,设 1 ⩽ x ⩽ min ⁡ ( r − i + 1 , r i − l + 1 ) 1\leqslant x\leqslant \min(r-i+1,r_{i-l+1}) 1xmin(ri+1,ril+1)
s i + x − 1 = s l + ( i + x − l ) − 1 s_{i+x-1}=s_{l+(i+x-l)-1} si+x1=sl+(i+xl)1
= s 1 + ( i + x − l ) − 1 = s i + x − l =s_{1+(i+x-l)-1}=s_{i+x-l} =s1+(i+xl)1=si+xl(左边的限制条件)
= s 1 + ( i − l + 1 ) + x =s_{1+(i-l+1)+x} =s1+(il+1)+x
= s 1 + x =s_{1+x} =s1+x(当 x ≤ z i − l + 1 x\leq z_{i-l+1} xzil+1

然后我们再暴力扩展,然后维护最大 r r r 对于的 l , r l,r l,r
然后每个数只会别暴力扩展一次,所以复杂度是 O ( n ) O(n) 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值