为什么要学习kmp and 算法的作用
给出两串 字符串,模式串T(长度m),匹配串S(长度n)。在S中快速找到T串的位置
暴力破解 是 O(n * m) ——枚举起始点逐个匹配
kmp算法可以在线性时间O(n + m)下解决问题
算法原理
- kmp根据 前缀表 和 next数组 来实现
前缀表是取出 T 串的所有前缀
- 对每一个前缀(后称pre)进行如下操作:
找出pre的前缀 和 pre的后缀,他们的长度均小于等于 len(pre) (即 不能是原串pre)
找出 当 pre前缀 == pre后缀 时,的最大长度
- 如何递推求出这个前缀表:
假设我们已经知道 某一段字符串,当新增下一个字符变成newPre时,分两种情况
为了方便例子的演示,1 和 2 中配图内字符不完全相同(橙色表示匹配成功的当前pre的前后缀)
1. 新增字符 == id下标(len(pre的前缀 )+ 1) 的字符
这时候直接 len + 1 即可
2.新增字符 != id下标(len(pre的前缀 )+ 1) 的字符
那么回退匹配串 到 上一层的最大匹配,继续比较,直到匹配成功,or回退致空串
到此为止 我们将后缀表的原理弄清楚了 后缀表 的原理
那么后缀表整理为
每个字符下是 pre 的 最长匹配 maxL
a b a a b c a c 0 0 1 1 2 0 1 0 next数组则整体右移 1 位,第一个字符下 填补 -1
这就是next数组
a b a a b c a c -1 0 0 1 1 2 0 1 这些数字就是索引的下标,如果匹配S串的过程中(S[ i ]),出现不匹配
那么直接跳转到这个T[id] 字符下 数字 id[ j ] 的 的字符处,再次比较 T[id [ j ] ] 和 S[ i ] 是否匹配,依次列推
直到匹配成功,或者 跳到-1 (即无处可跳,这里的跳就是 缩短pre的串 的一个过程)
代码:
#include<bits/stdc++.h> using namespace std; const int maxn = 1e5 + 7; const int inf = 1e9 + 7; int nxt[maxn]; char S[maxn], T[maxn]; void getNxt(char *str, int len){ nxt[0] = -1; int k = -1; for(int q = 1 ; q <= len - 1 ; q++){ while(k > -1 && str[k+1] == str[q]){ k = nxt[k]; } if(str[k+1] == str[q]){ k++; } nxt[q] = k; } } int KMP(char *str, char *ptr){ //menset(nxt, 0, sizeof(nxt)); int slen = strlen(str); int plen = strlen(ptr); getNxt(ptr, plen); int k = -1; for(int i = 0 ; i < slen ; i++){ while(k > -1 && ptr[k+1] != str[i]){ k = nxt[k]; } if(ptr[k+1] == str[i]){ k = k+1; } if(k == plen-1){ return i - plen + 1; } } return -1; } int main() { KMP(S , T); }