KMP改进主要是对next数组进行改进
如图当s[3]与p[3]不匹配时,根据next[3]=1,所以继续把s[3]与p[1]进行比较,即把模式串右移j-k=3-1=2位,此时s[3]与p[1]还是不等。其实仔细想一想,这是必然的,为什么呢?
因为当j=3时,p[j]=p[next[j]]即p[3]=p[1],问题就出在这里,当s[i]!=p[j],又因为p[j]=p[next[j]],所以p[next[j]]!=s[i]。所以可以对程序进行优化。如果出现了p[j] = p[ next[j] ],则需要再次递归,即令next[j] = next[ next[j] ]。
优化之前
public static int [] getNext(String p){
int plen=p.length();
char[] parray = p.toCharArray();
int [] next=new int [plen];
next[0]=-1;
int k=-1,j=0;
/**
首先要明白next[j]=k是什么意思,简单概述就是:当主串s和模式串p比较时,当s[i]!=p[j]时,
(我们不采用蛮力法时)我们的主串s的s[i]字符接下来该和模式串的p[k]字符进行比较,即我们
不再是像蛮力法那样,每次把模式串右移一个字符,再回溯到主串的下一个字符进行比较。采用KMP
算法,我们不需要回溯主串,而是直接把模式串右移j-k(j-next[j])个位置,进行下次比较。
也就是说当s[i]!=p[j]的时候,接下来我们让s[i]和p[k](p[next[j]])进行比较。
那么我们我们应该怎么求解next数组呢?已知next[j]=k,我们可以采用递归的方式求next[j+1]的值
注意next[j]=k意味着p[0]p[1]p[2]....p[k-1]=p[j-k]....p[j-1],=左右各有k个值
所以当求next[j+1]的时候,就需要比较p[0]p[1]p[2]....p[k-1]p[k]=p[j-k]....p[j-1]p[j]
又因为p[0]p[1]p[2]....p[k-1]=p[j-k]....p[j-1]相等,所以只需要比较p[k]和p[j]
假如p[k]=p[j],则next[j+1]=k+1,
假如p[k]!=p[j],令k=next[k],此时再比较p[j]与p[k],假如如果不相等,则继续递归前缀索引,
令 k=next[k],继续判断,直至k=-1(即k=next[0])或者p[j]=p[k]为止。
*/
while(j<plen-1){//对于数组next长度为plen所以当一个循环不满足条件时,j=plen-1,刚好是next数组的最后一个。
if(k==-1||parray[j]==parray[k]){//因为不可能p[0]和p[-1]比较,所以用k==-1
k++;
j++;
next[j]=k;//next[j+1]=k+1
}else{
k=next[k];
}
}
return next;
}
优化之后的
public static int[] getNext1(String p){
int plen=p.length();
char[] parray = p.toCharArray();
int [] next=new int[plen];
next[0]=-1;
int k=-1,j=0;
while(j<plen-1){
if(k==-1||parray[j]==parray[k]){
k++;
j++;
if(parray[j]!=parray[k]){//相当于判断p[j]!=p[next[j]]
next[j]=k;
}else{
next[j]=next[k];
}
}else{
k=next[k];
}
}
return next;
}