KMP算法
1. 笔算next数组
- 如果数组是从1开始
- max{前缀和后缀相等的长度} + 1为当前字符的next值
- 如果数组从0开始
- **max{前缀和后缀相等的长度}**为当前字符的next值
例如
a | b | a | b | c | a | b | a | |
---|---|---|---|---|---|---|---|---|
i | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
k | 0 | 1 | 1 | 2 | 3 | 1 | 2 | 3 |
kt | -1 | 0 | 0 | 1 | 2 | 0 | 1 | 3 |
例如对于i = 5时
后缀为: b, ab, bab
前缀为: a, ab, aba
最大相同前缀为 ab,长度为2所以为 k = 3
同理, 如果下标从0开始就是kt。
2. 代码
//求数组最大匹的前后缀的过程就是求next数组的过程。
i = 0;
j = next[0] = -1; //用来保存当前字符最大匹配的前后缀是多少,永远指向最大匹配的前缀所在
while(i < s.length()){
if(T[i] == T[j] || i == -1){
i++;
j++; //如果字符相等,那么最大匹配数就是j + 1
next[i] = j;
} else {
j = next[j]; //反之,如果不相等,就用上一个的最大匹配来实验
}
}
例如 :
i = 4, 5, *, *, 6
j = 2, 3, 1, 0,1
3. 优化
对于 aaaab,
next[] = {0, 1, 2, 3, 4}
但是如果 2 失败,回溯到1仍旧失败,回溯到0还是会失败。
就是比较最大前后缀之后看看这个字符是不是和匹配前缀的后一字符相等,如果相等就回溯。
4.优化代码
i = 0;
j = next[0] = -1;
//求每个字符的最大匹配的前后缀
while(i < s.length()){
if(j == -1 || T[i] == T[j]){
i++;
j++;
if(T[i] != T[j]){
next[i] = j;
}else next[i] = next[j];
} else {
j = next[j]; //回溯
}
}
5.完整代码
//回溯法进行匹配
char t[] = {'a','a','a','a','b'}; //匹配串
char s[] = {'c','d','a','a','a','a','b',}; //原串
int next[256];
void get_next(int length){
int i = 0, j = -1;
next[0] = -1;
while(i < length){
if(j == - 1 || t[i] == t[j]){
i++;j++;
if(t[i] != t[j])next[i] = j;
else next[i] = next[j];
}else j = next[j];
}
}
int KMP(int sl, int tl){
int i = 0, j = 0;
while(i < sl && j < tl){
if(j == -1 || s[i] == t[j]) {
i++;
j++;
}else j = next[j];
}
if(j == tl)return i - j;
else return -1;
}
6. 练习
- 子串
- 字符串的拼接
s.append()
- 字符串的增加字符
s.push_back()
- 使用进制转换与字符串的结合问题
- 字符串的拼接