参考文献:算法导论
实际匹配过程引用自:http://ds.fzu.edu.cn/fine/resources/FlashContent.asp?id=40
一般能想到
一个字符串比配问题,我们可能最长能想到的就是回溯的比较两个串,例如
abcacabababcb和ababc两个串进行比较
通常的做法是:
- 从第一个字符开始比较,abcacabababcb 和 ababc比较,发现相同
- abcacabababcb 和 ababc比较,发现相同
- abcacabababcb 和 ababc比较,发现不同
- 从第二个字符开始比较,abcacabababcb 和 ababc比较,发现不同
- 从第三个字符开始i比较
- …
如果我们进一步思考一下,就会发现,这个算法的效率实在是太低,每次只能步进一个字符,那么能不能步进多个字符?这样,我们比较的次数就减少,效率也就提升了如此,引出了KMP算法。
KMP算法
我们观察一下上面例子中的模式串”ababc”,发现这个串有一些规律,0、1位置字符和2、3位置字符相同,那么如果我们比较到3位置(第二个”b”),发现字符不同,也就说明我们比较了”aba”是相同的,我们发现配置串中第二个”a”与第一个”a”一样,也就说明第二个a有1位前缀与其相同。
那么我们知道了比较了到4个字符,其中前3个相同,那么我们需要移动几个字符呢?kmp告诉我们,我们需要移动3-1=2个位置,3及为相同字符数(也可以理解为),1即为模式串最后一个相同字符的的前缀数(第二个”a”有一个前缀,即为第一个”a”,也可以理解为与第一个字符相同),也就是说我们要至少要跳过”ababc”,也就是说这两位是一定不同的(例如”a1a2 _”这样一个字符串,1,2位置不确定想不相同,所以第一个a移动到第二个a的位置,然后比较1,2位置是否相同)。
那么通过上面这段话(不知道大家能否理解,如果不理解,请回复我在做解释)
我们可以将KMP分为2个步骤,第一步整理模式串,第二步实际匹配。
—————————————————————下面为一段乱入的文字———————————————————————
KMP和有限自动机一样,需要对模式串有预处理,但是KMP对模式串的预处理时间复杂度为O(m),而有限自动机的时间复杂度为O(m|∑|),显然KMP是一种更为高效的算法
和有限自动机一样,KMP也需要预先处理一个函数π,该函数的处理思想为设置一个变量来保存上一次匹配的最长前缀k,即表示到上一次字符串处理结束为止,模式串长度为k的前缀和其长度为k的后缀相同,也就是说这一次我们只要比较第k+1个字符和当前遍历到的字符如果相同,则说明有长度为k+1的前缀和长度为k+1的后缀相同
例如:ababa
如果上一次遍历到第3个字符和第1个字符相同,则这一次只要比较第4个字符和第2个字符相同则说明——第1,2个字符和第3,4个字符相同
—————————————————————乱入的文字结束——————————————————————————-
模式串预处理过程
abababac
(从第二个字符开始匹配,红色表示前缀,绿色表示后缀,黄色表示相交的部分,蓝色表示两个字母比较)
第1次:a != b , k = 0 , (串:ab , k=0则下次还从第一个字符开始匹配)
第2次:a = a , k = 1 , (串:aba , k=1则下次从第二个字符开始匹配)
第3次:b = b , k = 2 , (串:abab , k=2则下次从第三个字符开始匹配)
第3次:a = a , k = 3 , (串:ababa , k=3则下次从第四个字符开始匹配)
第4次:b = b , k = 4 , (串:ababab, k=4则下次动第五个字符开始匹配)
…
由此可见,其整理过程只遍历了一次模式串
实际匹配过程
大家可以参考这个:http://ds.fzu.edu.cn/fine/resources/FlashContent.asp?id=40
其中偏移距离计算公式为:s = i – π[i-1](i为第一次不同的位置,则i-1为最后一次相同的位置)