字符串匹配是计算机的基本任务之一,而Knuth-Morris-Pratt算法(简称KMP)是最常用的之一。它以三个发明者命名,起头的那个K就是著名科学家Donald Knuth。之前课堂老师讲过KMP算法的原理,但是半年过去忘的也差不多了,这次重新复习希望以后可以把它记住。话不多说,进入正题。
---------------------------------------------------------------------------------------------------------------------------
在说KMP算法之前,先说一下BF算法。
我们给定两个字符串: 主串 “ababcabcabd” 模式串:“abcabd”
BF算法的匹配如下:
——>
——>
——>
——>
——>
我们可以看到,当匹配开始进行,模式串与主串的前两个字符匹配成功,但第三个字符匹配失败。这时就需要将主串后移,并让模式串回到第一个字符的位置。让模式串的第一个字符从主串本次匹配位置的下一位开始匹配,直到匹配成功或者匹配失败。这就是BF算法的过程。
我们再来观察主串和模式串的关系,我们看第三张匹配的图片,图片中“abcab”字符都已匹配成功,当匹配到第6个字符‘c’时匹配失败,在BF算法中,它下次进行的操作是将主串从开始匹配位置+1,模式串重置。但是我们看图片就可以知道,通过本次匹配我们已经知道在主串不匹配位置‘c’字符之前字符为“abcab”,其最后两位字符“ab”与模式串前两位“ab”是匹配的,那么我们下次是不是可以不让模式串重置,继续让模式串后移,直接让模式串的第三位‘c’与主串中的‘c’进行匹配,这样就可以少进行两次匹配,这就是KMP算法的思想。
---------------------------------------------------------------------------------------------------------------------------
下面是我在百度百科上面找到的关于KMP算法的介绍:
KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt同时发现,因此人们称它为克努特——莫里斯——普拉特操作(简称KMP算法)。KMP算法的关键是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是实现一个next()函数,函数本身包含了模式串的局部匹配信息。
j | 0 | 1 | 2 | 3 | 4 | 5 |
W[j] | a | b | a | c | a | b |
F(j) | 0 | 0 | 1 | 0 | 1 | 2 |
---------------------------------------------------------------------------------------------------------------------------
下面就是我个人对于KMP算法的理解:
在模式串中头部与后面的部分是有重复的字符的,比如上述例子“abcabd”中“ab”就是重复的。我们还需要了解两个概念,“前缀”和“后缀:"前缀"指除了最后一个字符以外,一个字符串的全部头部组合;"后缀"指除了第一个字符以外,一个字符串的全部尾部组合。我们需要找的就是前缀和后缀最长的共有字符串。比如上面的例子“abcabd”中最长相同字符串就为‘ab’。
当主串中出现不匹配字符时,就从主串不匹配位置的上一位开始,寻找这个最长相同字符串,如果找到了,那么下次这些匹配过的就不用再进行匹配了。比如上面的例子“abcabd”中最长相同前缀就为‘ab’,因此在第三张图片的过程结束后,我们可以直接进行下面的步骤: 也就是直接进行上面例子中最后一张图片中的内容。
对于模式串的每个字符,都需要有一个值来表示如果在此位置出现不匹配了,那么下次应该用模式串中的哪一位字符来与主串的不匹配位置进行比较,也就是next()函数的值,这个值只是模式串本身的属性,与主串无关。通过上述的解释我们很容易就能得出这个函数的值的表了。
对于上述例子中的模式串我们可以得到它的next()表为:
-1:第一位就匹配失败,返回-1意思为主串向后+1。其余数字均为下标。
当主串中出现不匹配时,只需要让模式串中不匹配的字符跳转到next(),然后用next()对应的字符继续与主串的不匹配字符比较即可。比如上述例子:当主串中的‘c’与模式串中的‘d’不匹配时,通过next()我们知道,只需要让‘c’继续与模式串的next(2),也就是‘c’比较即可。
再举几个例子:
参考:
http://www.ruanyifeng.com/blog/2013/05/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm.html