字符串(KMP系列)
一. KMP
1. border
如果一个字符串的前缀等于他的后缀,我们称这个字符串是原字符串的border。
例如,abcakmeabc 中,abc 就是一个border。
2. KMP
定义一个字符串的 数组表示:前 个字符的最长border长度。
那怎么求一个字符的nxt呢?代码如下:
void kmp(int n,string b)
{
int j=0;
for(int i=2;i<=n;i++)
{
while(j&&b[i]!=b[j+1]) j=nxt[j];
if(b[j+1]==b[i]) j++;
nxt[i]=j;
}
}
时间复杂度 。
KMP经常用来求解模式串匹配的问题。即询问 在 中出现的次数。一般来说,代码如下:
j=0;
for(int i=1;i<=m;i++)
{
while(j>0&&b[j+1]!=a[i])
j=nxt[j];
if(b[j+1]==a[i])
j++;
if(j==n)
{
cout<<i-n+1<<endl;
j=nxt[j];
}
}
其中,。
原因自己上题解搜。
二. exKMP/扩展KMP/Z函数
1. 什么是 Z 函数
注:字符串从 开始编号。
什么是 函数呢?我们定义 表示 与 的每一个后缀 的最长公共前缀(LCP)的长度,此时我们称 为 的 函数。特殊的,。
举几个栗子:
aaaaa;aaabaab。
2. Z 函数线性求法
就像 一样,Z 函数也可以在 复杂度中求。
首先假设已知 ,现在要求 。
如果 被求出来了,那么 一定是 的前缀,且 一定不是 的前缀。那么现在正在处理 ,区间 就称为我们的匹配段,简称 Z-box。
由于 太难敲了,所以我们简称 是我们的匹配段。由于左端点已知,我们只需要扩展右端点。根据定义, 是 的前缀。我们维护的时候 ,初始 。
分两种情况讨论
- 当前处理的 在 的左边():由于是匹配段,那么同一长度的 肯定是相等的!(因为他们都是 的前缀,长度又相等,必然相同)就有性质:。所以 ?别忘了, 的长度一定不超过 (隔着超过字符串长度了?)所以 。
- 如果 ,那么 。
- 如果 ,那么 的基础上,暴力扩展 。
- 如果 ,暴力扩展
最后,如果 ,则令 即可。
3. 代码
for(int i=1;i<n;i++)
{
int len=r-i+1;
if(i<=r&&z[i-l]<len) z[i]=z[i-l];
else
{
z[i]=max(0ll,len);
while(i+z[i]<n&&a[z[i]]==b[i+z[i]]) z[i]++;
}
if(i+z[i]-1>r) l=i,r=i+z[i]-1;
}
4. 时空复杂度分析
容易发现,内层 每次至少加一,外层 每层加一。总的时间复杂度 。

浙公网安备 33010602011771号