KMP算法中next数组的性质
next数组性质:对于某一字符串S[1~i],在它众多的next[i]的选项中,若存在next[i],有i%(i-next[i])==0,那么S[1~(i~next[i])]可以为S[1~i]的循环元而i/(i-next[i])即为其循环次数。
那么如何对模式串S求解其next数组呢?我们先把C++代码摆上先来一点小小的视觉震撼
int n;
cin>>n;
char a[n+1];
cin>>a+1;
int next[n+1];//设置next数组
next[0]=-1;
for(int i=2,j=0;i<=n;i++)
{
while(j&&a[i]!=a[j+1])
{
j=next[j];
}
if(a[i]==a[j+1])j++;
next[i]=j;
}
我们发现这个求next数组的代码特别简洁。所以不要害怕,我们接下来说明他的原理
我们以模式串“abcabcd“为例(下角标从1开始):
i=2,即指向b,此时j为0指向下角标0(一个我们不考虑的角标),然后开始判断:j=0,故循环条件的j&&a[i]!=a[j+1]为0,代表这个i之前的没有相同的前缀后缀。然后就前后缀都是0.开始判断a[i]==a[j+1],此时j+1为1指向a,i为2指向b,故j保持不变。next[2]=0;
i=3同理;得next[3]=0;
i=4时我们发现while循环不执行,但是!a[4]==a[0+1].所以j++得到j=1.表示此时对于“abca”最大公共前缀后缀长为1.得next[4]=1;
i=5.,我们发现a[5]==a[1+1],所以j++,然后next[5]=2;(即“abcab”)得最大公共前后缀长为2
i=6,发现a[6]=a[2+1];j=3,最大公共前后缀长为3。
那么当a[i]!=a[j+1]得时候呢?
i=7,此时j=3.我们发现a[i]!=a[j+1]。需要执行while语句中得j=next[j].即j=next[3]=0.这段代码是想要找到比当前最大公共前后缀第二长得公共前后缀,然后进行比较a[i]!=a[j+1].如果发现没有就得到j=0.(代表得含义是你这新加得字符我找不到有前缀与你这个当前后缀相同得前缀长度)。此时就在找到得前缀得后一个比较是否与新增得字符相等。若相等则j++,next[i]=j.表示当前前i个字符串中最新得最大公共前后缀长度为j。
然后求完next数组之后就是应用其进行模式串在字符串中得匹配,代码如下:
char p[M+1];//p存储得是要被匹配得串,M是该字符串长度
char a[n+1];//模式串,n为模式串长度
//上述两个字符串均为从1开始
for(int i=1,j=0;i<=M;i++)
{
if(p[i]==a[j+1])//被匹配串与模式串直到当前位置均一样
{
i++;j++;
}
else //有不一样得字符了,就需要用到next数组了
{
//此时主串第i个字符与模式串第j个字符不同。那么我们需要如何尽量避免从头开始比较呢?
//i不用回退,需要根据next数组回退j
while(a[i]!=a[j+1]&&j)
{
j=next[j];
}
if(a[j+1]==a[i])j++;
}
}
主要思想为,当前主串和模式串依次匹配,然后若正确匹配就+1.否则就根据next数组找到有没有前缀与当前这个加上不匹配当前最大前缀匹配得前缀。