KMP算法
2016-05-20 22:53
246 查看
以前在一本数据结构书中看到了KMP算法,大概懂了其意思,但一直没有看懂其具体的实施过程。最近又看了一下,大概明白了一些,故写出来,与大家分享一下我的想法,希望读者不吝赐教。
这个算法是大家最容易想到,也是最容易理解的。它是将主串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位置继续向右进行比较,后续的比较同理。
其计算公式如下图:
假设有一个如下图的模式串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代码来找值:
本人菜鸟一枚,代码就将就着看吧!
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对世界各个时区(TimeZone)的通用转换处理方法(转载)
- java-注解annotation
- java-模拟tomcat服务器
- java-用HttpURLConnection发送Http请求.
- java-WEB中的监听器Lisener
- Android IPC进程间通讯机制
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- 介绍一款信息管理系统的开源框架---jeecg
- 聚类算法之kmeans算法java版本
- java实现 PageRank算法
- PropertyChangeListener简单理解
- c++11 + SDL2 + ffmpeg +OpenAL + java = Android播放器
- 插入排序
- 冒泡排序
- 堆排序
- 快速排序
- 二叉查找树