看了好几遍模式匹配kmp算法,始终没有看明白,今天总算有些头绪了,写下来。(ps:用的是严蔚敏的数据结构,感觉这本书还不错)
1.普通的模式匹配
这里先简单地说一下普通模式匹配算法的思想,不做过多的解释。
例如:
主串 a b c a b a b c c c a b c a b d
模式串 a b c a b d
主串中的第i个字符与模式串中的第一个字符进行比较,如果相同则继续比较下一个字符,如果不同,主串从i+1个字符开始,和模式串做匹配,直到所有字符全部匹配上结束。
这个算法容易理解但是效率比较低,在上例中第一次匹配时abcab已经匹配,那可不可以根据现有的信息,向右尽可能多的移动模式串进行下一次的匹配呢?答案是可以的,于是有了KMP算法。
2.KMP算法
我们可以看出上例中第一次匹配失败的时候主串c应该和模式串中的a进行下一次的比较。但是计算机应该怎么知道呢?先解决这个问题。
当主串中第i个字符和模式串中第j个字符匹配失败的时候,主串中的第i个字符应该与模式串中哪个字符进行比较?
讨论一般情况,设:
主 串: ’ s1 s2 s3 s4 … si si+1 si+2 … sn’
模式串:’p1 p2 ….pj pj+1 pj+2 ….pm’
假设此时应该讲模式串回溯到pk位置和主串进行比较,则在pk之前的字符和主串一定是匹配的。
图1
图2
图2中k之前的部分满足:
‘p1 p2 p3 …pk-1’ = ‘si-k+1 sI-k+2 … si-1’
图1中已匹配的部分满足:
‘pj-k+1 pj-k+2 ….pj-1’ = ‘si-k+1 si-k+2 … si-1’
由上面两个式子可以得出:
‘p1 p2 … pk-1’ = ‘pj-k+1 pj-j+2 … pj-1’
如果模式串中存在满足上式的两个子串,则当主串中i和模式串中j不相等时,将模式串向右移动至k与i对齐,进行下面的匹配。
下面用一个next[]数组记录模式串中每个字符不匹配是需回溯的k点。
下面解释一下’p1 p2 … pk-1’ = ‘pj-k+1 pj-j+2 … pj-1’这个式子怎么找max值
设一个模式串为:
a b a a b c 并假设字符串编号从1开始
‘p1 p2 … pk-1’ 这个式子 下面以“左式”代替
‘pj-k+1 pj-j+2 … pj-1’这个式子 下面以“右式”代替
j = 1 时 next[1] = 0
j = 2 时 k 只能取1 容易看出next[2] = 1
j = 3 时 k 取 1 没有子串,(以后不再讨论这种情况)
………….k 取 2 时:左式为 p1=a 右式为p3-2+1=a 所以k=2时成立,此时next[3] = 2
j = 4 时 k 取 2 时:左式为p1=a 右式为p4-2+1=a 所以k=2时成立
………….k 取 3 时:左式为p1p2=ab 右式为p4-3+1 p4-3+2=ba 所以k=3时不成立,此时next[4] = 2
以此类推。
在网上看到另一种比较简单的算法(来自阮一峰的网络日志):
原文地址:http://www.ruanyifeng.com/blog/2013/05/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm.html
仍然拿 a b a a b c 举例
a 的前缀后缀为空集,
ab 前缀为a 后缀为b, 最长的相等子集长度为0
aba前缀为 a ab 后缀为ba a 最长的相等子集长度为1
abaa前缀为a ab aba 后缀为baa aa a最长的相等子集长度为1
abaab前缀为a ab aba abaa 后缀为 baab aab ab b 最长的相等子集长度为2
abaabc前缀为a ab aba abaa abaab 后缀 baabc aabc abc bc c最长的相等子集长度为0
得到的部分匹配值为:0 0 1 1 3 0
移动位数 = 已匹配的字符数 - 对应的部分匹配值