Z函数(扩展KMP)

 1,定义

z函数存储字符串s(长度n,下标从0开始)与其所有后缀s[i,n-1](0<=i<=n-1)的最大公共前缀LCP的值(一般默认z[0]=0,有时是n)

2,思路

叫他扩展KMP是有原因的,因为思想相近,我们求取z[i],尝试利用前面求取过的z函数,通过作图我们发现,当我们维持一个区间(匹配段)[L,R],使得s[0,h]==s[L,R](h==R-L+1),我们让指针i从0到n-1遍历,不断维持[L,R](当然,L<=i,初始L=R=0,并且尽可能让r大(因为l一定小于i,我们让r大就可以)

2c0eaff8119045e2913b5efa114e6cad.png

1,发现当i<=r时,s[i,R]==s[i-L,R-L],所以此时求z[i],等价于求模板s与后缀s[i-L,R-L]的LCP,即z[i]=z[i-L](当然,前提是z[i-L]<R-L+1,匹配区间不可以超出(图中黄色字))。

2,那么超出区间的话,我们只能先让z[i]=R-L+1,然后暴力比较后面部分。

3,如果i>r,无法取巧,也只能暴力

得到简洁代码

const int N = 2e5 + 100;
char s[N];
ll z[N];
int main()
{
	scanf("%s", s);//长字符串用scanf,否则cin容易被tle(除非解绑了cin)
	int len = strlen(s);
	z[0] = 0;//默认z[0]=0
	for (int i = 1, l = 0, r = 0; i < len; ++i)//i从下标1开始,因为0是规定的,初始l=r=0
		{
			if (i <= r && s[i - l] < r - i + 1)z[i] = z[i - l];//情况一,i<=r并且不超过区间(等于也不可以,因为刚刚好等于,不确定r的下一位是否相等,没有判断),可以直接继承z[i-l]
			else
				{
					z[i] = max(0, r - i + 1);//取0,说明i>r,取r-i+1,说明超出匹配区间
					while (i + z[i] < len && s[z[i]] == s[i + z[i]])++z[i];//暴力匹配后面部分
				}
			if (i + z[i] - 1 > r)l = i, r = i + z[i] - 1;//尽量让r大,有就更新r(还有l)
		}
	return 0;
}

3,复杂度O(n)

首先i线性n,里面while观察到,最多进行n次,因为每次都会让r增大,而r小于n

4,模板题:P5410 【模板】扩展 KMP(Z 函数)

思路:

b的z函数,无脑写就是

a关于模板b的在函数,适当修改即可

#include <bits/stdc++.h>
using namespace std;
#define ll     long long
typedef unsigned long long ull;
typedef pair<long long, long long> pll;
const int INF = 0x3f3f3f3f;
const int N = 2e7 + 10;

char a[N], b[N];
ll a1[N], b1[N];

int main()
{
	scanf("%s%s", a, b);
	int la = strlen(a), lb = strlen(b);
//a对模板b,取z[i-l],取的是模板部分的z函数,所以我们先求出b的z函数
	b1[0] = lb;//这题规定z[0]=n
	for (int i = 1, l = 0, r = 0; i < lb; ++i)
		{
			if (i <= r && b1[i - l] < r - i + 1)b1[i] = b1[i - l];
			else
				{
					b1[i] = max(0, r - i + 1);
					while (b1[i] + i < lb && b[b1[i]] == b[i + b1[i]])++b1[i];
				}
			if (b1[i] + i - 1 > r)l = i, r = i + b1[i] - 1;
		}
//暴力找a关于模板b的z[0]
	for (int i = 0; i < la; ++i)
		{
			if (b[i] == a[i])++a1[0];
			else break;//不等于直接跳出,不能待着
		}

	for (int i = 1, l = 0, r = 0; i < la; ++i)
		{
			if (i <= r && b1[i - l] < r - i + 1)a1[i] = b1[i - l];//注意,a对模板b,取z[i-l],取的是模板部分的z函数,所以是判断b的z函数的值,取也是b的z函数的值
			else
				{
					a1[i] = max(0, r - i + 1);
					while (i + a1[i] < la && b[a1[i]] == a[i + a1[i]])++a1[i];
				}
			if (i + a1[i] - 1 > r)l = i, r = i + a1[i] - 1;
		}

	ll as = 0, bs = 0;
	for (int i = 0; i < la; ++i)as ^= (i + 1) * (a1[i] + 1);
	for (int i = 0; i < lb; ++i)bs ^= (i + 1) * (b1[i] + 1);
	cout << bs << endl << as << endl;
	return 0;
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值