4.2. 串的模式匹配
4.2.1. 简单的模式匹配算法
4.2.2. KMP 算法
例如,想要从 ababcabcacbab
中匹配 abcac
,用暴力法就会出现下面的情况
索引 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
---|
原串 | a | b | a | b | c | a | b | c | a | c | b | a | b |
第 1 次 | a | b | c | | | | | | | | | | |
第 2 次 | | a | | | | | | | | | | | |
第 3 次 | | | a | b | c | a | c | | | | | | |
第 4 次 | | | | a | | | | | | | | | |
第 5 次 | | | | | a | | | | | | | | |
第 6 次 | | | | | | a | b | c | a | c | | | |
可以发现,这种情况下,效率就相对比较差。我们希望在副串移动时直接移动到合适的位置,并且 i 不应该回溯。
索引 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
---|
原串 | a | b | a | b | c | a | b | c | a | c | b | a | b |
第 1 次 | a | b | c | | | | | | | | | | |
第 2 次 | | | a | b | c | a | c | | | | | | |
第 3 次 | | | | | | a | b | c | a | c | | | |
右移位数 = 已匹配的字符数 - 对应部分的匹配值
使用部分匹配值时,每当匹配失败,就去找他前一个元素的部分匹配值,这样有些别扭,那就不妨把 PM
表右移一位,这样哪个元素匹配失败,直接看他自己的部分匹配值即可。
a | b | c | a | c |
---|
-1 | 0 | 0 | 0 | 1 |
于是,上式改写成
相当于回退到
模式串与 next
数组
4.2.3. KMP 算法的优化
next
数组在某些情况下仍然有一些缺陷,还可以进一步优化,例如 aaaab
与 aaabaaaab
的匹配
中间多了 3 次多余的匹配,因此我们可以考虑将它优化。
观察到,当 s[3] != p[3]
时,如果用 next
数组,还需要 s[3]
与 p[2]
, p[1]
, p[0]
分别匹配。
此时应当注意到,不应该出现 p[j] == p[next[j]]
,因为当 p[j] != s[j]
时,下一次比较必然是 p[next[j]]
和 s[j]
,因此需要 递归 一下,将 next[j]
修正为 next[next[j]]
,直至两者不相等为止。