首先感谢2014年一位博主写的博客
写的很详细
不过有一点小小的错误
我在评论区改正了
我就在此基础上用图来表示
我把大家视为啥都不懂的小白
现在我们有一个字符串A
我们有一天突然想要知道对于A的每一位开始
最多能和多长的A的前缀完全相等?
比如我们可爱的ywwyww字符串
就是这样的一个数组:6 0 0 3 0 0
第0位开始和原来的字符串完全相同,所以是6
第1位开始就和y不同
第2位开始就和y不同
第3位开始和原来的前缀yww相同,所以是3
……
现在搞懂了我们想要搞懂的问题
我们该怎么在线性时间里把这个数组求出来呢?
首先我们探求一下这个数组的性质
假设这个数组名字叫做next(编码的时候不要用next命名,因为next在stl里有定义)
另t=next[i]
那么next[i]的意义就是说A[0]到A[t-1] 和 A[i]到A[i+t-1] 的字符完全相同
比如上面的栗子就是i等于3时next[3]=3。代表A[0]到A[2] 和 A[3]到A[5]的字符完全相同。
我们完成了第一步
第二步我们开始探求递推关系
首先next[0]肯定是A数组的长度啦
next[1]可以通过下方代码求出
int a=0,Tlen=strlen(A),next[0]=Tlen;
while(a<Tlen-1&&T[a]==T[a+1]) a++;
next[1]=a;
下面就是我们的假设环节
假设我们已经知道了next[0]到next[k-1]的数值
0到k-1呢存在一个数a
使得0<=a<=k-1条件下a+next[a]-1最大
稍微想一下就知道max(a+next[a]-1)就是我们的前缀能够匹配的最大距离
我们令p=max(a+next[a]-1)
好的,现在我们想求next[k]
咋办呢?
我们画一个数轴,把所有已知量给写出来
纵方向相对的就是相同的字符
理解这个后
我们引入L=next[k-a]这个量
可能你现在觉得这个什么鬼量啊,等会你就清楚了
我们简单分析一下
next[k-a]代表什么呢?
代表A[0]到A[L-1] 和 A[k-a]到A[k-a+L-1]的所有字符相同~
现在我们想一想k-a+L-1在数轴里什么位置呢?
我们重画一下
第一种情况:k-a+L-1<=p-a,就是 k+L-1<=p
数轴变成了这样
又由我们刚才得到的next[k-a]的作用:A[0]到A[L-1] 和 A[k-a]到A[k-a+L-1]的所有字符相同~
而通过数轴我们看出A[k]到A[k+L-1] 和 A[k-a]到A[k-a+L-1] 的所有字符相同!
所以得出结论:A[0]到A[L-1] 和 A[k]到A[k+L-1] 的所有字符相同~
所以我们的next[k]的范围肯定>=L,对吧
现在想想next[k]有可能大于L吗??
思考一下如果next[k]>L,那么A[k+L]就要和A[k-a+L]相同
那么A[L]就要和A[k-a+L]相同!!!
那么我们的next[k-a]就要变大了
这显然不可能
所以我们的next[k]就定调了
当k+L-1<=p时,next[k]=L。
如果看懂了前面的
我们进入第二种情况:k-a+L-1>p-a,就是 k+L-1>p
此时数轴是这样的
那么我们同样有这个结论:A[0]到A[L-1] 和 A[k]到A[k+L-1] 的所有字符相同~
L含义同上
此刻我们分析一下由于k+L-1>p,所以p-k<=L-1
诶?此刻我们再分析一下
由于p-k<=L-1
A[0]到A[p-k]是不是和 A[k]到A[p]相同了?
是不是很惊喜?
我们的next[k]的范围不就是>=p-k+1了嘛
此时想想
能不能大于p-k+1呢?
答案是可以的
因为从A[p+1]开始我们就没有再匹配过了
也就无从知晓是否匹配
于是我们开始最笨的方法
一个一个匹配
最终一个不匹配或者超出了Tlen范围
那么next[k]就出来了
在这个过程中,a,p,L是一直在维护的
这个真的搞了我一天。。。
附上代码
void pre_kmp(char s[]){
memset(nextv,0,sizeof(nextv));
int len=strlen(s);
nextv[0]=len;
int j=0;
while(j<len&&s[j]==s[j+1])j++;
nextv[1]=j;
int a=1;
for(int k=2;k<len;k++){//要从2开始
int L=nextv[k-a],p=a+nextv[a]-1;
if(k+L-1<p){//注意等于的时候就要重新匹配
nextv[k]=L;
}else {
j=max(0,p-k+1);
while(k+j<len&&s[k+j]==s[j])j++;//是k+j<len
nextv[k]=j;
a=k;
}
}
}
那么我们有两个字符串A和B的话,对于A的每一位开始
最多能和多长的B的前缀完全相等?
我们是不是只需要预处理B的next数组,就可以求出这个问题了?
void GetExtend(char *S,char *T)
{
int a=0;
GetNext(T);
int Slen=strlen(S);
int Tlen=strlen(T);
int MinLen=Slen<Tlen? Slen:Tlen;
while(a<MinLen&&S[a]==T[a]) a++;
extend[0]=a;
a=0;
for(int k=1;k<Slen;k++)
{
int p=a+extend[a]-1,L=next[k-a];
if((k-1)+L>=p)
{
int j=(p-k+1)>0? p-k+1:0;
while(k+j<Slen&&j<Tlen&&S[k+j]==T[j]) j++;
extend[k]=j;
a=k;
}
else extend[k]=L;
}
}
小结:我看到几乎所有博客都是直接讲extend数组,不去讲next数组,所以我一直犯迷糊,所以我以next数组讲,希望对大家有所帮助
真搞了我一天,jio的好走一波点赞关注?