概念
KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt同时发现,因此人们称它为克努特——莫里斯——普拉特操作(简称KMP算法)。KMP算法的关键是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是实现一个next()函数,函数本身包含了模式串的局部匹配信息。时间复杂度O(m+n)。
算法思路
1、主串不需要回滚
2、子串通过子串本身的一种性质,调整j的位置,再继续与i比较
3、时间复杂度由暴力破解的O(m*n)提高到O(m+n)
解题思路
1、如果是只需要理解(做选择题啊什么的)
首先我们要明白三个单词的意思:
前缀:指的是必须包含第一个字符,但是不包含最后一个字符的字符组。
后缀:指的是必须包含最后一个字符,但是不包含第一个字符的字符组。
next:每个串对应的最大的相同值。
现在的next数组失配时是用前一个匹配的位置找对应的j的新位置,但是算法要求是当前位置找对应的新位置。
所有直接将原next数组整体右移一位,next[0]定义成-1,如下图:
我们再来看个串匹配的例题:
下面是串匹配的步骤:
这样我们的值就匹配完成了,对于我们平时做题,如果是选择题,那么答案就出来了,但是一般都是会要求我们写出源码。这时候,我们就要进行更深层次的理解。
2、编码
首先我们要理解next[j],它既是最大前缀或后缀值,也是跳到的数组下标。
(1)设置j指向前缀,i遍历子串,指向后缀(其实也就是看作是主串和子串是一样的匹配过程)
(2)如果p[i]==p[j]或者j==-1相等则,i++;j++;next[i]=j(再次注意,j是下标的同时,也是最大前缀者)
(3)如果p[i]!=p[j],j=next[j];如果p[j]与p[i]还是不相等,继续循环跳next[j]
核心代码:
public static int[]getNext(char[]p){
int[]next=new int[p.length];
next[0]=-1;
int j=0;
int i=1;
while(i<p.length-1) {
if(j==-1||p[i]==p[j]) {
++j;
++i;
next[i]=j;
}else {
j=next[i];
}
}
return next;
}
public int kmp(char str[],char p[],int next[]) {
int i=0,j=0;
int n=str.length;
int m=p.length;
for(i=0;i<n;++i) {
while(j>-1&&str[i]!=p[j]) {
j=next[j];
}
if(j==-1||str[i]==p[j]) {
j++;
}
if(j==m) {
return i-j+1;
}
}
retrun -1;
}