-
KMP算法,全称Knuth Morris Pratt算法
-
核心思想
-
KMP目的
-
最长可匹配后缀子串 && 最长可匹配前缀子串
-
失效函数(next 数组)

- 数组的下标是每个前缀结尾字符下标,数组的值是这个前缀的最长可以匹配前缀子串的结尾字符下标
- 例子:ababacd
- 前缀列表访问顺序:从右到左
- 后缀列表访问顺序:从左到右
- 过程
1. a: 无匹配,下标为-1 2. ab: 无匹配,下标为-1 3. aba: 匹配1个字符。下标为0 前缀: a ab 后缀: ba a 4. abab,匹配2个字符,下标为1 前缀:a ab aba 后缀:bab ab b 5. ababa,匹配3个字符,下标为2 前缀:a ab aba abab 后缀:baba aba ab a 6. ababac,无匹配,下标为-1 前缀:a ab aba abab ababa 后缀:babac abac bac ac c 7. ababacd,无匹配,下标为-1 前缀:a ab aba abab ababa ababac 后缀:babacd abacd bacd acd cd c
- 移位位数: 已匹配的字符数 - 对应的部分匹配值
-
next数组的计算
- 暴力计算方法
- 类动态规划方法(k:最长前后缀子串)
- 若p[k] == p[i]
- 若p[k] ≠ p[i]
-
时间复杂度
- 构建next数组
void getNext(char *p, int p_len, int *next) { next[0] = -1; int k = -1; int i; for (i = 1; i < p_len; ++i) { while (k != -1 && p[k + 1] != p[i]) { k = next[k]; } if (p[k + 1] == p[i]) { ++k; } next[i] = k; } }- i 从1开始一直增加到p_len,而k并不是每次for循环都增加,所以,k累积增加的值肯定小于 p_len
- 而while循环中的 k = next[k],实际上是在减小k的值,k累积都没有增加超过p_len.所以while循环总数也不会超过p_len
- 这部分时间复杂度: O(p_len)
- 借助next数组匹配
int kmp(char *s, int s_len, char *p, int p_len) { int next[p_len]; getNext(p, p_len, next); int j = 0; int i; for (i = 0; i < s_len; ++i) { while (j > 0 && s[i] != p[j]) { // 一直找到s[i] 和 p[j] j = next[j - 1] + 1; } if (s[i] == p[j]) ++j; if (j == p_len) { // 找到匹配模式串 return i - p_len + 1; } } return -1; }- i 从0循环增加到 s_len - 1, j的增长量不可能超过i,所以肯定小于s_len
- 而while 循环中的那条 j = next[j - 1] + 1; 不会让 j增长
- 所以,这部分的时间复杂度为O(s_len)
- 总时间复杂度: O(s_len + p_len)
- 构建next数组
-
空间复杂度
- KMP只需要一个额外的next数组,数组的大小跟模式串相同
- 空间复杂度:O(p_len), p_len表示模式串长度







