Z 函数(扩展KMP)

简介

定义一个长度为 n 的字符串 s,定义 zi 表示 ss[i,n]lcp(最长公共前缀),称 zsZ 函数。

朴素算法

暴力枚举,时间复杂度:O(n2)

线性算法

我们首先考虑顺次处理 z1,z2,,zn,当枚举到 i 时,也就是说 z1,,zi1 是已经求得了。

我们再来定义名词 Z-box :指与字符串 s 匹配的前缀区间,同时满足包含 i (当枚举到 i 时)且右端点最大。

首先我们来考虑如何更新这个 Z-box (分类讨论):

  1. l 为左端点(i1 的 Z-box),r 为右端点(i1 的 Z-box),i 为当前枚举的点。

  2. i>r 时,那也就是上一个 Z-box 对于 i 时没有用处的,那就从 i 开始,暴力向后匹配求得 Z-box

  3. ir 时,分两种情况:

    • zil+1<rl+1

      img

      那么 s[1,z[il+1]]=s[i1+1,il+z[il+1]]=s[i,i+z[il+1]1] (图标可能有点问题,以公式为准!!!)

    • zil+1rl+1

      那么 s[1,rl+1]=s[l,r],但是后面的匹配不能保证所以就向后匹配即可。

时间复杂度证明

枚举 iO(n) 的,r 会一直向后移动也是 O(n) 的,所以总的来说是 O(n) 的。

代码实现

inline void getz()
{
	for(int i=1;i<=m;i++)z[i]=0;
	z[1]=m;
	for(int i=2,l=0,r=0;i<=m;i++)
	{
		if(i<=r)z[i]=min(z[i-l+1],r-i+1);
		while(i+z[i]<=m&&b[i+z[i]]==b[z[i]+1])++z[i];
		if(r<i+z[i]-1)l=i,r=i+z[i]-1;
	}
}

同时这个也可拓展到两个串的匹配(st 的每一个后缀的 lcp),代码实现:

inline void getext()
{
	for(int i=1;i<=n;i++)ext[i]=0;
	for(int i=1,l=0,r=0;i<=n;i++)
	{
		if(i<=r)ext[i]=min(z[i-l+1],r-i+1);
		while(i+ext[i]<=n&&b[ext[i]+1]==a[i+ext[i]])++ext[i];
		if(r<i+ext[i]-1)l=i,r=i+ext[i]-1;
	}
}
posted @   tyccyt  阅读(109)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 实操Deepseek接入个人知识库
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· 【.NET】调用本地 Deepseek 模型
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
点击右上角即可分享
微信分享提示