扩展KMP (Z 函数)

一些约定:

  • 字符串下标从 1 开始。
  • s[1,i] 表示字符串 s 的第一个到第 i 个字符组成的字符串。

Z 函数

我们定义:

  • z[i] 表示最大的 len 使得 B[1,len]=B[i,i+len1]。其中 z[1]=m

  • Zbox:这是我们在求 z 数组时维护的一段区间,用两个变量 l,r 表示,它表示目前为止 B 中右端点最大的一个区间 [l,r] 满足,B[l,r]=B[1,rl+1]

知道了一些定义我们就来看 z 数组怎么求。
假设我们已经得出 z[1] z[i1] 了要求 z[i],且目前的 Zbox=[l,r]

  1. lir (实际上如果 ir , i 一定满足 lir):

r 表示 rl+1,则 B[1,r]=B[l,r] (为了下面叙述方便,我们称 rr 的对应点),图片中相同颜色代表这段区间相同。

我们再求出 i 的对应点 i=il+1,则 B[i,r]=B[i,r]

假设 z[i]=lenB[1,len]=B[i,i+len1],现在又有两种情况:

  • lenri+1: 此时 B[1,len]=B[i,i+len1]=B[i,i+len1],又因为 i+len1 并未超过 Zbox 的右端点,所以必有 z[i]=len

  • 而如果 len>ri+1,如下图

我们无法确定绿色部分是否相同,因此不能直接把 len 赋给 z[i],但我们可以保证 z[i]ri+1r 后面的部分我们暴力扫描判断。

  1. i>r: 同样也是暴力往后扫描即可。

每次求完 z[i] 后如果 i+z[i]1>r 则用 ii+z[i]1 更新 l,r

z 数组的代码如下:

	z[1]=m,l=0,r=0;
	for(int i=2;i<=m;i++)
	{
		if(i<=r) z[i]=min(z[i-l+1],r-i+1);
		while(b[i+z[i]]==b[1+z[i]]) z[i]++; //如果i>r那这里z[i]一开始是0
		if(i+z[i]-1>r) r=i+z[i]-1,l=i; 
	}

复杂度分析: 会发现一旦出现暴力遍历的情况必然会更新右端点 r,而右端点只能更新 O(n) 次,再 r=n 之后就不会出现暴力扫描的情况了,因此复杂度是 O(n)

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

求解 p 数组的过程是差不多的。

Code:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e7+5;
inline int read(){
    int w = 1, s = 0;
    char c = getchar();
    for (; c < '0' || c > '9'; w *= (c == '-') ? -1 : 1, c = getchar());
    for (; c >= '0' && c <= '9'; s = 10 * s + (c - '0'), c = getchar());
    return s * w;
}
char a[N],b[N];
int n,m;
string s1,s2;
int z[N],p[N];
int l,r;
int ans1,ans2;
signed main()
{
	cin>>s1>>s2;
	n=s1.size(),m=s2.size();
	for(int i=1;i<=n;i++) a[i]=s1[i-1];
	for(int i=1;i<=m;i++) b[i]=s2[i-1];
	z[1]=m,l=0,r=0;
	for(int i=2;i<=m;i++){
		if(i<=r) z[i]=min(z[i-l+1],r-i+1);
		while(b[i+z[i]]==b[1+z[i]]) z[i]++;
		if(i+z[i]-1>r) r=i+z[i]-1,l=i; 
	}
	for(int i=1;i<=m;i++){
		ans1^=(z[i]+1)*i;
	}
	l=0,r=0;
	for(int i=1;i<=n;i++){
		if(i<=r) p[i]=min(z[i-l+1],r-i+1);
		while(a[i+p[i]]==b[1+p[i]]&&i+p[i]<=n&&1+p[i]<=m) p[i]++;   
			//注意这里要判断边界 
		if(i+p[i]-1>r) r=i+p[i]-1,l=i;
	}
	for(int i=1;i<=n;i++){
		ans2^=(p[i]+1)*i;
	}
	printf("%lld\n%lld\n",ans1,ans2);
	return 0;
}

本博客参考的网址:

[C++]洛谷:【模板】扩展 KMP(Z 函数) 算法详解

posted @   Green&White  阅读(12)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示