KMP查找
1.概述
KMP算法的核心是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是通过一个next()函数实现,函数本身包含了模式串的局部匹配信息。KMP算法的时间复杂度O(m+n)。
2.算法
设主串与子串如下:
主串 | a | b | c | a | b | c | e | a | b | c | a | b | c | d | a | b | c | a | b | c | f |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
子串 | a | b | c | a | b | c | d | a | b | c | a | b | c | f |
- 常见的字符串匹配:当主串与子串在逐字符匹配时一旦没有匹配成功,则让主串回跳到第一个位置重新开始比较,时间消耗很大。
- 而KMP子串匹配:当某一位置匹配不成功时,主串不回跳使子串回跳,(但如果子串回跳到第一个位置,也会浪费一定的时间),KMP采用Next数组来解决这一问题。
Next数组的实现就需要引入字符串的前后缀
前缀:以当前串开头为开头的串,不能包括字符串本身。
后缀:以当前串结尾为结尾的串,也不能包括字符串本身。
最大匹配长度:前缀与后缀相等的最大字符个数。
试求出此字符串的前缀、后缀与前后缀最大匹配长度:ababa
前缀 | a | ab | aba | abab |
---|---|---|---|---|
后缀 | a | ba | aba | baba |
由此可以看出此串最大匹配长度为3。
再回过头来看KMP匹配:
Next数组就是求子串的前后缀匹配最大长度
例如:
- s2[0] a 前后缀最大匹配长度为:0
- s2[1] ab 前后缀最大匹配长度为:0
- s2[2] abc 前后缀最大匹配长度为:0
- s2[3] abca 前后缀最大匹配长度为:1
- s2[4] abcab 前后缀最大匹配长度为:2
- s2[5] abcabc 前后缀最大匹配长度为:3
- s2[6] abcabcd 前后缀最大匹配长度为:0
d和s2[13]:f 所面对的情况一样,肉眼很容易看出他的最大匹配长度为0,然而用代码实现并不是那么简单,你需要找到前一个字符所对应的Next数组,如果不为0你需要跳到它的Next数组所对应的s2的位置,如果为0你需要跳到子串开头位置,将此字符与其进行比较,如果都不相同,此时最大匹配长度才为0。
这么干说可能不太理解,用s2[6]='d’做例子。Next[5]=3,'d’则要和s2[3]='a’做比较,不相等。虽然s2[6]与s2[3]不相等,但他们所对应的前面的位置是相等的,设’d’现在所在位置为s2[3],Next[2]=‘0’,’d’则要和s2[0]='a’做比较,不相等,而s2也走到了字符串头的位置,这时才能说Next[6]=0,也就是说’d’所对应前后缀最大匹配长度为0
- 后面情况类似…
下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
主串s1 | a | b | c | a | b | c | e | a | a | b | c | a | b | c | d | a | b | c | f |
子串s2 | a | b | c | a | b | c | d | a | b | c | f | ||||||||
Next | 0 | 0 | 0 | 1 | 2 | 3 | 0 | 1 | 2 | 3 | 0 |