KMP模式匹配算法

来源:互联网 发布:java如何打war包 编辑:程序博客网 时间:2024/06/06 02:22

字符串匹配的问题应该大家经常遇到的,那么同样,我也遇到了这样的一些问题,也有一些心得和大家分享一二。


我们假设一个字符串匹配的很一般的背景:已知S、T两个字符串,其中S为原字符串,T为需要我们匹配的字符串,主要任务就是完成到S中匹配T字符串。如果能够匹配到的话返回对应的字符串的位置。

 

我们假设原字符串长度为M,字串的长度为N。


1. 首先我们先看朴素匹配的方法:

主要内容:原理不多做介绍,本质而言就是从原字符串开始搜索,如果出现不能匹配,原搜索位置后移一位(我们默认搜索的方向是从前往后,从左往右)。

附:java实现

public static int  BFSearch(String original,String find){int len1=original.length();int len2=find.length();int j=0;int i=0;while(j<len2 ){if(original.charAt(i)!=find.charAt(j))i++;else{i++;j++;}}return i-j;}

2. 

在这个基础上面我们考虑能不能对算法做些优化呢?如果优化的话应该在哪里优化呢?

我们下面来分析上面的过程,上面的字符串匹配的过程中,我们会注意到每次匹配尝试失败之后需要回溯,也就是需要从匹配的原字符串的位置往后移动一位考虑,那么在某些情况下,这样的操作会不会明显有问题呢?

答案是某些情况下面我们可以进行优化。


在这里 感谢其他网友的示例图片 


http://www.ituring.com.cn/article/59881


通过上面的例子我们可以发现,我们在匹配的时候,最后的一位不合适的时候其实我们可以直接从第五个位置开始匹配,为什么呢?

因为abcde的字符串第一个到第四个各不相同,第五个没有和原字符串匹配成功,意味着什么?意味着前面的四个是匹配的,那么当我把字符串匹配的初始位置向后移动一位、两位、三位、四位肯定是不匹配的。相信看到这里你们已经抓住了问题的关键:分析目标字符串本身的性质。换言之,用一句比价朴素的话来讲就是,通过分析目标字符串的相似部分的情况,然后在回溯的时候减少回溯的步数,更少的回溯意味着更高的效率。

 

kmp算法的核心即是计算字符串f每一个位置之前的字符串的前缀和后缀公共部分的最大长度。获得f每一个位置的最大公共长度之后,就可以利用该最大公共长度快速和字符串O比较。当每次比较到两个字符串的字符不同时,我们就可以根据最大公共长度将字符串f向前移动(已匹配长度-最大公共长度)位,接着继续比较下一个位置。事实上,字符串f的前移只是概念上的前移,只要我们在比较的时候从最大公共长度之后比较f和O即可达到字符串f前移的目的。


在这里引入一个示例来说明这个问题,示例中一个重要的方面是创建对应的next数组,示例中对于前缀和后缀对于next数组的产生有着重要的作用。

示例地址:

http://blog.csdn.net/buaa_shang/article/details/9907183

作者:阮一峰

http://www.ruanyifeng.com/home.html

博客地址:

http://www.ruanyifeng.com/blog/2013/05/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm.html


通过上面的一个示例,相信大家对于整个KMP算法如何计算应该有了比较详细的了解。我就不通过语言进行多余的赘述了,我个人更喜欢通过图形化的方式记下相关的算法的原理,最后附上相关的代码:


public static void Search(String original,String find,int next[]){int j=0;for(int i=0;i<original.length();i++){while(j>0&&original.charAt(i)!=find.charAt(j))j=next[j];if(original.charAt(i)==find.charAt(j))j++;if(j==find.length()){System.out.println("find at pos "+(i-j+1));System.out.println(original.subSequence(i-j+1, i+1));j=next[j];}}}public static  int[] getNext(String b){int len=b.length();int j=0;int next[]=new int [len+1];////next 表示长度为i的字符串的前缀和后缀的最长公共部分 从1开始next[0]=next[1]=0;for(int i=1;i<len;i++){while(j>0&&b.charAt(i)!=b.charAt(j))j=next[j];if(b.charAt(i)==b.charAt(j))j++;next[i+1]=j;}return next;}public static int  BFSearch(String original,String find){int len1=original.length();int len2=find.length();int j=0;int i=0;while(j<len2 ){if(original.charAt(i)!=find.charAt(j))i++;else{i++;j++;}}return i-j;}


如有问题  欢迎指正 欢迎交流。

最后还有相关的博客作为个人整理的资料的参考,提供给大家,非常感谢下面的博客的博主带来的明细的讲解。

1.  http://www.ruanyifeng.com/blog/2013/05/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm.html

2   http://www.ituring.com.cn/article/59881

3.  http://blog.csdn.net/yutianzuijin/article/details/11954939

4.  http://blog.csdn.net/WINCOL/article/details/4795369






0 0
原创粉丝点击