不为难自己,迟早会委屈自己!

KMP字符串匹配算法

KMP字符串匹配算法

/编辑

KMP完全匹配算法和 Levenshtein相似度匹配算法是模糊查找匹配字符串中最经典的算法,配合近期技术栏目关于算法的探讨,从网上摘取了一些简要的内容,加上自己的一些理解,向大家普及一些这方面的知识,希望能抛砖引玉。

l  算法简介:

kmp算法是一种改进的字符串匹配算法,由D.E.KnuthV.R.PrattJ.H.Morris同时发现,因此人们称它为克努特——莫里斯——普拉特操作(简称KMP算法)。KMP算法的关键是根据给定的模式串W1,m,定义一个next函数。next函数包含了模式串本身局部匹配的信息。

l KMP算法和传统算法的匹配比较:

1.      传统算法:

         传统匹配思想是,从目标串Target的第一个字符开始扫描,逐一与模式串的对应字符进行匹配,若该组字符匹配,则检测下一组字符,如遇失配,则退回到Target的第二个字符,重复上述步骤,直到整个PatternTarget中找到匹配,或者已经扫描完整个目标串也没能够完成匹配为止。假设模式串Pattern长度为m,目标串Target长度为n,则每次比较最多需要花费O(m)的时间,算法的复杂度为O((n-m+1)*m)。这种算法没有利用匹配过的信息,每次都从头开始比较,速度很慢。

2.      KMP算法:

在传统算法的基础上,当匹配失败后,并不简单地从目标串下一个字符开始新一轮的检测,而是依据在检测之前得到的有用信息,或者说模式串本身的特征信息,取得next函数跳转的位置,直接跳过不必要每次从头检测,从而达到一个较高的检测效率。

l 算法实例说明:

假设在串S=abcabcabdabba中查找T= abcabd

1.      传统算法:

先是比较S[0]和T[0]是否相等,然后比较S[1] 和T[1]是否相等我们发现一直比较到S[5] 和T[5]才不等。如图:

 

当这样一个失配发生时,T下标必须回溯到开始,S下标回溯的长度与T相同,然后S下标增1,然后再次比较。如图:

这次立刻发生了失配,T下标又回溯到开始,S下标增1,然后再次比较。如图:


这次立刻发生了失配,T下标又回溯到开始,S下标增1,然后再次比较。如图:



又一次发生了失配,所以T下标又回溯到开始,S下标增1,然后再次比较。这次T中的所有字符都和S中相应的字符匹配了。函数返回TS中的起始下标3。如图:

2.      KMP算法:

还是相同的例子,在S=abcabcabdabba中查找T=abcabd,如果使用KMP匹配算法,当第一次搜索到S[5]T[5]不等后,如图:

S下标不是回溯到1T下标也不是回溯到开始,而是根据TT[5]==d的模式函数值(next[5]=2),直接比较S[5] T[2]是否相等,因为相等,ST的下标同时增加;因为又相等,ST的下标又同时增加。。。最终在S中找到了T。如图:

此处关键的问题是,为什么KMP算法能聪明的知道应该直接从S[5] T[2]开始比较呢?因为它能够利用已经得到的部分匹配信息来进行过滤。因为我们根据第一次比较得到的结果,如图:

很明显已经匹配了到了部分结果”abcab”,只是由于到T[5]==’d’的时候才无法和S[5]=’c’匹配上的,此时按传统算法T[0]应该重新开始和S[1]比较,但是根据之前的匹配结果S[1]等于T[1],但很明显T[0]不等于T[1],因此比较T[0]S[1](也即T[1])是完全没有必要的。

            以此类推,根据已经得到的结果”abcab”T[0]重新开始比较的时候,应该和S[3](也即T[3])开始比较才是明智的。

因此 next[5]=2这个2表示T[5]==d的前面有2个字符和开始的两个字符相同,且T[5]==d不等于开始的两个字符之后的第三个字符(T[2]=c.如图:

 

也就是说,如果开始的两个字符之后的第三个字符也为d,那么,尽管T[5]==d的前面有2个字符和开始的两个字符相同,T[5]==d的模式函数值也不为2,而是为0

附件为KMP算法的实现java,有兴趣的同事可以试试:

KMPMatchString.java

 



来自为知笔记(Wiz)


posted on 2015-04-27 17:47  Dimmacro  阅读(2960)  评论(0编辑  收藏  举报