KMP算法的基础应用
本篇博客来源于https://www.cnblogs.com/yjiyjige/p/3263858.html,内容中含有大量原文,仅是本蒟蒻的一篇笔记
KMP算法指的是字符串模式匹配算法,问题是:在主串T中找到第一次出现完整子串P时的起始位置。该算法是三位大牛:D.E.Knuth、J.H.Morris和V.R.Pratt同时发现的,以其名字首字母命名。本篇博客讲介绍KMP算法的最基础应用。
一
一般字符串匹配问题的解法:
对于这种字符串匹配问题,通常的思路是一个一个字母的匹配直到完全重合,如果不匹配则将下面的关键字向右移动一位,直到遍历所有或者找到完全匹配的字符串。
二
显然这很暴力。比如,对于串“SSSSSSSSSS”和串“SB”来讲,我们要找到最后才能得到答案,即没有与之匹配的串。大牛忍受不了这种低效暴力的算法,一起研究出了KMP算法,整个算法的核心就是利用已经“部分匹配”的信息,更快找到答案。所以,整个KMP的重点就在于当某一个字符与主串不匹配时,我们应该知道j指针要移动到哪?
三
接下来我们自己来发现j的移动规律:
如图:C和D不匹配了,我们要把j移动到哪?显然是第1位。为什么?因为前面有一个A相同啊:
如下图也是一样的情况:
可以把j指针移动到第2位,因为前面有两个字母是一样的:
至此我们可以大概看出一点端倪,当匹配失败时,j要移动的下一个位置k。存在着这样的性质:最前面的k个字符和j之前的最后k个字符是一样的。如果用数学公式来表示是这样的
P[0 ~ k-1] == P[j-k ~ j-1]
四
该规律是KMP算法的关键,KMP算法是利用待匹配的子串自身的这种性质,来提高匹配速度。该性质在许多其他中版本的解释中还可以描述成:若子串的前缀集和后缀集中,重复的最长子串的长度为k,则下次匹配子串的j可以移动到第k位(下标为0为第0位)。我们将这个解释定义成最大重复子串解释。
这里面的前缀集表示除去最后一个字符后的前面的所有子串集合,同理后缀集指的的是除去第一个字符后的后面的子串组成的集合。举例说明如下:
在“aba”中,前缀集就是除掉最后一个字符’a’后的子串集合{a,ab},同理后缀集为除掉最前一个字符a后的子串集合{a,ba},那么两者最长的重复子串就是a,k=1;
在“ababa”中,前缀集是{a,ab,aba,abab},后缀集是{a,ba,aba,baba},二者最长重复子串是aba,k=3;
在“abcabcdabc”中,前缀集是{a,ab,abc,abca,abcab,abcabc,abcabcd,abcabcda,abcabcdab},后缀集是{c,bc,abc,dabc,cdabc,bcdabc,abcdabc,cabcdabc,bcabcdabc},二者最长重复的子串是“abc”,k=3;