KMP算法原理

来源:互联网 发布:淘宝化妆品店铺推荐 编辑:程序博客网 时间:2024/04/29 11:41

字符串匹配是最计算机科学的基本研究问题之一,在文档检索、生物信息等领域有着举足轻重的地位。简单来说字符串匹配问题就是:如何确定某字符串是否包含另一字符串。暴力搜索算法(Brute Force, BF)是最容易想到和实现的,但是它高达O(n^2)时间复杂度难以满足许多场景。KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt同时发现。KMP算法的特点就是利用了匹配失败后的信息,达到了减少模式串和主串匹配次数的目的,是一种广泛应用的高效匹配算法。


假定待匹配主串为T,模式串(pattern)为P。例如:


在讲KMP之前我们首先来看一下暴力搜索算法(Brute Force, BF)是怎样执行的。

首先是第一趟匹配,从T[0]和P[0]开始:

发现某个字符不相同,终止此次匹配,进行下一趟匹配:

照此方法依次匹配下去,直到匹配成功或者到最后一个字串匹配失败返回。显然暴力搜索的复杂度为O(n^2),尤其是当主串为“aaaaaaaaaaaaaaab”,模式串为“aaaab”这类形式,匹配次数达到最大。

那么怎样才能改进暴力搜索算法呢?我们注意到,暴力搜索过程的每趟匹配失败后就开始下一趟匹配,并且每次匹配都是从头开始重新匹配,如果我们能够利用每次匹配失败的信息,就能够大大减少重复劳动。这就是KMP方法的基本思想。

首先要引入前缀表(prefix table)的概念:
对于模式串,它的前缀就是从第一个字符开始的某个字串,而前缀表就由这些字串导出。例如模式串P,它的前缀分别是:

我们不考虑前缀是模式串自身的情况,即将最后一行删去。

下一步就是找出每个前缀的最长公共前后缀的长度,比如前缀“aba”的最长公共前后缀为“a”,长度为1;前缀“abab”的最长公共前后缀为“ab”,长度为2。
将得到的所有长度值列出来,并在最前面加上一个-1,得到一个前缀序列:

我们得到了前缀序列:-1,0,0,1,2,然后将前缀表对应模式串写出来,就得到了前缀表:


再一次开始匹配算法。与暴力搜索一样,我们先从T[0],P[0]开始。不同的是模式串标注了前缀表信息(红色)和下标信息(蓝色):


第一趟匹配:

匹配到第四个字符时发现a!=b,这时不像暴力匹配那样直接进行下一趟匹配,而是查看前缀表,找到匹配失败处的数字:1,则下次匹配就从模式串下标1的字符开始:

下一趟匹配(主串绿色字符表示已经匹配过):

发现第一个字符a!=b,查看前缀表,发现对应的是0,则下次匹配从模式串下标为0的字符开始:
(可以看出,从第一趟匹配到第二趟匹配,直接跳过了三个字符(绿色),比暴力搜索的优势就在这里体现)

失败处前缀表对应的是0,下趟匹配从模式串下标为0的字符开始:

失败处前缀表对应的是-1,下趟匹配从模式串下标为-1的字符开始(模式串-1位置就是第一个字符前的假定位置,匹配时直接从下标为0处开始即可):

失败处前缀表对应的是2,下趟匹配从模式串下标为2的字符开始......
当匹配成功后,继续搜索,把模式串的最后一位当作匹配失败位,找到其前缀表对应的数字,继续上述匹配循环。
继续匹配直到搜索到最后,退出KMP算法。


原创粉丝点击