网上已经有大量关于KMP的博客了,我也写不出新意来,这里就不写KMP的原理,需要的可以去看看其他博客,这里主要聚焦一个问题:如何在知道KMP的原理后,快速写出代码?
对于KMP算法,我觉得应该牢记以下几条内容,以便能够快速写出代码:
- 失配时,文本串不回溯,模式串回溯
- 模式串回溯时依赖 next 数组
- next 数组的计算只依赖模式串
- 假设模式串为P,已知 next[i] = k 的情况下,求 next[i + 1] 的方法:若 P[i] == P[k],则 next[i + 1] = k + 1,否则视模式串在 P[k] 处失配,此时可以将 P[i] 理解为文本串当前正在匹配的字符,在这种情况下,应该将文本串的 P[i] 与模式串的 P[next[k]] 进行比较,若 P[i] == P[next[k]],则 next[i + 1] = next[k] + 1,否则重复上面的过程,让 P[i] 与 P[next[next[k]]] 进行比较,直到找到符合条件的 next 数组项,若 next 数组中不存在使得上述等式成立的项,则让 next[i + 1] 等于模式串中第一个元素的下标。
总之,通过迭代的方式可以求出 next 数组,下面是求 next 数组的示例,欢迎指正:
vector<int> generate_next(string pattern)
{
int i = 0, k = 0, n = pattern.size();
vector<int> next(n);
next[0] = -1;
for (i = 1; i < n; ++i) {
k = next[i - 1];
while (k != -1) {
if (pattern[k] == pattern[i - 1]) {
break;
}
k = next[k];
}
next[i] = k + 1;
}
return next;
}
字符串匹配示例:
int find_string(string text, string pattern)
{
vector<int> next = generate_next(pattern);
int i = 0, j = 0, k = 0, n = text.size(), m = pattern.size();
for (i = 0; i < n; ++i) {
if (text[i] == pattern[j]) {
++j;
if (j == m) {
return i - m + 1;
}
continue;
}
k = next[j];
while (k != -1) {
if (text[i] == pattern[k]) {
break;
}
k = next[k];
}
j = k + 1;
}
return -1;
}