您的位置:首页 > 编程语言 > Java开发

KMP算法

2016-05-20 22:53 246 查看
以前在一本数据结构书中看到了KMP算法,大概懂了其意思,但一直没有看懂其具体的实施过程。最近又看了一下,大概明白了一些,故写出来,与大家分享一下我的想法,希望读者不吝赐教。

KMP算法简介

KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt同时发现,因此人们称它为克努特——莫里斯——普拉特操作(简称KMP算法)。KMP算法的关键是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是实现一个next()函数,函数本身包含了模式串的局部匹配信息。(摘自百度百科)

算法原理

1.朴素匹配算法 
这个算法是大家最容易想到,也是最容易理解的。它是将主串s与模式串t进行逐个比对,当主串的某位与模式串的不相等时,主串的指针位置不变,将模式串的指针拉到起始位置。再继续向前进行比较直到成功或者失败。显然这符合大家思维,但是它的问题就出现了,当字符串很长时,它的效率是极低的,期间进行了多次重复比较,产生大量的冗余。而KMP算法就是减少多次重复的比较,加快效率。
2.KMP算法 
将主串与模式串进行逐个对比,当主串的第i个和模式串的第j个元素不相等的时候,则说明模式串的前(j-1)个元素匹配成功,即可得方程等式:


(1)

则对于主串必然存在一个这样的等式,其中的k可能存在,也可能不存在,但是该等式仍然成立:


(2)

当k<j时,由(1)式可得以下等式: 


(3)
所以由(2)和(3)式可得:


 (4)

而且该式成立的条件是k<j。
由(4)式可以说明:在模式串从1-->j序列中必然存在一个k值,使得此序列的前k-1个元素与后k-1个元素一一对应且相等。
所以,当主串的第i个和模式串的第j个元素不相等的时候,主串的i不变,模式串向右滑动至其k位置,然后i位置和k位置继续向右进行比较,后续的比较同理。

next[]数组的构造

next[]数组是KMP算法的精髓所在,也是最难理解的部分。在网上看了一下该算法和别人解释,对于next[]数组的构造有不同的构造法,所以其值可能不同,但原理都是一样的。都是为了找到重复的那部分,找到那个k的位置。下面是我对其构造的一种理解和算法:其中next[]数组的值表示k的位置,即模式串与主串不相等时,next[]的值即为模式串应该向右滑动到的位置k。
其计算公式如下图:



假设有一个如下图的模式串t:



则按照上图next[]的构造规则,其值如下:



对上图next[] 的分析过程:

当j=0时,由上述计算公式的next[j]=0;
当j=1时,从0至j-1为:a,满足(4)式的最大k值为0;
当j=2时,从0至j-1为:ab,满足(4)式的最大k值为0;
当j=3时,从0至j-1为:abc,满足(4)式的最大k值为0;
当j=4时,从0至j-1为:abca,则a=a,满足(4)式的最大k值为1;
当j=5时,从0至j-1为:abcab,则ab=ab,满足(4)式的最大k值为2;
当j=6时,从0至j-1为:abcabc,则abc=abc,满足(4)式的最大k值为3;
当j=7时,从0至j-1为:abcabcd,满足(4)式的最大k值为0;
当j=8时,从0至j-1为:abcabcde,满足(4)式的最大k值为0;
当j=9时,从0至j-1为:abcabcdea,则a=a,满足(4)式的最大k值为1;
以上就是找k值得大概过程。都是根据公式在找,接下来就是通过Java代码来找值:
public static int[] getNext(char[] t){
int[] next=new int[t.length];

/*将m=0,n=1,当n位置(t
)和m位置(t[m])相等时,m和n同时前进,m即为该n处以前的最大k值;
* 当两者不相等时,只有n前进,则使m=0,返回0处。
* */
int m=0,n=1;
next[m]=m;          //使next[0]=0
while(n<t.length){
next
=m;      //m即为最大的k值
if(t
==t[m]){
m++;        //当t
==t[m]时,表示(0->m-1)段和(n-m -> n-1)段相等
}else{
m=0;        //不相等时,m=0
}
n++;
}
return next;
}


KMP算法代码

下面是根据我自己的理解写的一个KMP算法,主要包括匹配字符和获取next[]数组。
public class KMP {

public static void main(String[] args){
String S="asdhhsadfgkdfgasdfgghajcfkk";  //主串
String T="dfgkdfgasd";                   //模式串

char[] SArray=S.toCharArray();
char[] TArray=T.toCharArray();

int i=0,j=0;
int[] next=null;
next=getNext(TArray);
for(;i<S.length();i++){
if(j<T.length()){
if(SArray[i]==TArray[j]){
j++;           //匹配成功j++,继续向右进行
}else{
j=next[j];     //不成功,模式串向右滑动到k位置
}
}else{

break;
}
}
if(i==S.length()){
System.out.println("没找到!");
}else{
System.out.println(i);   //找到,输出模式串在主串中匹配成功的尾部位置
}

}

public static int[] getNext(char[] t){
int[] next=new int[t.length];
int m=0,n=1;
next[m]=m;
while(n<t.length){
next
=m;
if(t
==t[m]){
m++;
}else{
m=0;
}
n++;
}
//输出next[]数组结果
/*for(int k=0;k<t.length;k++){
System.out.print(t[k]+"  ");
}
System.out.println();
for(int k=0;k<next.length;k++){
System.out.print(next[k]+"  ");
}*/
return next;
}
}

本人菜鸟一枚,代码就将就着看吧!

总结

KMP算法确实在比较过程中省去了很多重复的比较动作,但其理解起来很难,自己也是第一次没看懂后来又多看了几遍,看了很多人的解释才懂了一些,而且当模式串比较短或者没有重复的字符时,和朴素匹配算法没有区别,意义不大。当模式串比较长且有较多重复段,则很适用,能节省很多时间。


紧是自己的学习与理解,有什么错误希望读者能评论出来,加以指正,以便更多的人探讨学习!

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java kmp 算法