什么是KMP算法:
KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt同时发现,因此人们称它为克努特——莫里斯——普拉特操作(简称KMP算法)。KMP算法的关键是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是实现一个next()函数,函数本身包含了模式串的局部匹配信息。时间复杂度O(m+n)。
现在我们呢来举一个例子把!
问题描述:
在"aaccbcaabaabcaccac"这个字符串中找"abaabcac",我们如果全凭视力来查找的话,估计很快就会找到答案,但是如果字符串特别长呢?你还用视力嘛?怕是行不通了。
其实这个模式串匹配算法很多,为什么要着重的将KMP算法呢?因为KMP算法效率高!
那么为什么KMP算法效率会高呢?当然效率高是相对的,像简单的BF算法也可以解决模式串匹配问题,但是其效率十分低下,没有将匹配失败带给我们的有用信息利用起来,而我们的KMP算法就是充分挖掘了失败的匹配带给我们的有用信息利用起来,实现了主串无回溯,提高匹配效率!
我相信大家都已经明白KMP算法是干什么的了,我在这里就直接给大家说干货了!
1.next数组
在我看来,KMP算法难就难在这个next数组上,我以前刚接触的KMP算法的时候,看了很多博客,但是我发现它们都有一个通病,他们会告诉你怎么求next数组,但不会告诉你为什么这么求,这就让我很不爽!
我们就求解一下上面的"abaabcac"所对应的next数组把!
如何求next数组呢?
我给出的解释就是:当前位置所对应的字符的 最大 真前缀=真后缀 再加上1,就是当前字符所对应的下标的next值!
其中的"T1...TK-1"就是当前字符所对应的最大真前缀,"Tj-k+1...Tj-1"就是其对应的最大真后缀串。
注意:一般数组的零号位置做监视哨,所以j=1就是当前数组的第一个存储元素所在位置。
第一个字符a:按照定义next[1]=0;
第二个字符b:按照定义,根本不存在符合条件的K,next[2]=1;
第三个字符a:按照定义,其真前缀有且仅有a,真后缀有且仅有b,且a!=b,next[3]=1;
第四个字符a:按照定义,可以看出 最大且满足 真前缀==真后缀 的只有a==a,next[4]=2;
第五个字符b:按照定义,可以就看出其next[5]=2;
第六个字符c:按照定义,可以看出,ab==ab,所以next[6]=3;
第七个字符a:按照定义,next[7]=1;
第八个字符c:按照定义,可以看出 a==a,所以next[8]=2;
接下来我们有算法实现求next数组:
//求next数组
public static int[] getNext(String[] model) {
//next数组
int[] next=new int[model.length];
int j=1;
int k=0;
next[1]=0;
while(j<model.length-1) {
if( k==0 || model[j]==model[k]) {
++j;
++k;
next[j]=k;
}else {
k=next[k];
}
}
return next;
}
这里我要说一下当Tk!=Tj时为什么是k=next[k],而不是别的。为什么要这样写呢?你想一想k是什么?是你的上一个字符的 真前缀=真后缀 的最大长度,我们现在已经失配了,我们能得到的最好结果就是看当前字符何next[k]处的字符是否匹配,next[k]代表什么?代表第K个字符,我们的前一个字符匹配到了K个长度,我们既然在第K加1个字符处失配,那么我们当前效率做高的做法就是看看能否实现K长度个字符的匹配!
下面是KMP算法的实现!
/**
* 模式串匹配算法KMP
* @author king
*/
public class Main_Operations {
//next数组
private int[] next;
//目标串(一般情况下字符串的零号元素做监视哨)
private String[] aim={"Listener","a","b","a","a","b","c","a","c"};
//待查串
private String[] model= {"Listener","a","a","c","c","b","c","a","a","b","a","a","b","c","a","c","c","a","c"};
//运行KMP算法
public void Run_Kmp() {
//获得next数组
next=Next_Operations.getNext(aim);
//待查串的当前匹配位置
int i=1;
//目标串当前匹配位置
int j=1;
while( i<model.length && j<aim.length ) {
if(j==0 || model[i]==aim[j]) {
i++;
j++;
}else {
j=next[j];
}
}
System.out.println(i-aim.length+1);
}
@Test
public void fun1() {
Run_Kmp();
}
}
谢谢观赏!