最近在学习KMP算法,现在记录下来,一方面是作为学习的总结,一方面加深自己的理解。
首先从问题出发,因为算法都是为了解决问题嘛。
问题描述:
有两个串 s 和 t , 子串 t 的定位就是要在主串 s 中找到一个与子串 t 相等的子串。
问题分析:
首先扯一些概念,我们把主串 s 成为目标串,把子串 t 称为模式串,而这个问题就是经典的模式匹配问题。
OK,切入正题,看到问题,第一个想法就是遍历嘛,从主串 s 的第一个字符开始,跟子串 t 中的字符一一比对,相等的话,就继续比对,
不相等的话,就从主串 s 的第二个字符开始,继续和子串 t 中的字符一一比对,如此循环,一直到在主串 s 中找到匹配串,或者到 主串 s 遍历
完。而这就是BF(Brute-Force)算法,这算法存在一个效率问题,就是主串的遍历存在大量的回溯,
例如:假设 s = "abcabdabcabcd" ,t = "abcabcd", 那么匹配过程如下:
第一次比对:
a b c a b d a b c a b c d
| | | | | |
a b c a b c d
第二次比对:
a b c a b d a b c a b c d
|
_ a b c a b c d
如上,一直匹配到主串 s 的第 6 个字符才失败,而第二次比对就又回溯到主串 s 的第 2 个字符重新开始匹配。
那么,有没有什么方法可以减少这种回溯呢?
还是分析上面那个例子,子串一直匹配到 ‘c’ ,即 “abcabc”,除了最后一个字符 ‘c’ 与主串 s 不匹配,前面的子串 “abcab” 都是和主串 s 匹配
的,那么下一次比对,我们就可以直接从主串中的下一个字符 ‘a’ 开始,也就是从 “abcabc” 中的从左向右数的第二个字符 'a' 开始,(因为在这
之前的比对肯定都是匹配失败的),如果从第二个字符 'a' 开始,往右的每个字符,一直到 最后一个字符 ‘c’ 的前一个字符为止,都能和子串从第
一个字符 ‘a’ 开始的字符一一匹配,那么下一次匹配就能直接从第二个字符 'a' 开始匹配了。
以上就是KMP算法的核心内容,整体思路就是从子串 t 下手,想方设法的减少对主串 s 的回溯,方法就是寻找子串 t 的某种规则,规则如下:
假设子串 t = "t0 t1 t2 t3 ... tn",,对于 t 中的任意字符tj, 找到这样一个整数k, 0<=k<j,使得 t0 t1 t2 ... tk = tk-1 tk tk+1... tj-1,而匹配子串 tk-1 tk tk+1... tj-1
必须是符合条件的最大匹配子串。这样,就可以利用这个最大匹配子串来尽量减少匹配过程中的回溯。
我们用一个数组next[] ,封装子串 t 中的每一个字符对应的最大匹配子串,并规定next[0] = -1, next[1] = 0,从next[2]开始,数组元素的值等于其
对应的最大匹配子串的字符数,还是分析上例, 子串 t = "abcabcd",那么next[] = {-1, 0, 0, 0, 1, 2, 3},这样就可以利用数组next[] 去减少回溯,
今天就写到这,下次再写吧。
本文深入探讨了KMP算法的核心思想及实现方式,通过对比BF算法,详细解释了如何利用KMP算法减少模式匹配过程中的回溯,提高匹配效率。
9万+

被折叠的 条评论
为什么被折叠?



