10.KMP
KMP
KMP 主要是处理这一类问题
给定一个文本串
和一个模式串 ,在 中找出 第一次出现的位置
暴力匹配算法
暴力很好想,从
int match(string s, string p) // 串匹配算法
{
int lens = s.length(); // 文本串长度
int lenp = p.length(); // 模式串长度
int i = 0, j = 0; // i指向文本串,j指向模式串,代表当前比对字符的位置
while (i < lens && j < lenp)
{
if (s[i] == p[j]) // 若匹配
{
i++;
j++; // 同时后移,跳转至下一个字符
}
else // 若不匹配
{
i -= j - 1; // 文本串回退
j = 0; // 模式串复位
}
}
return i - j; // 返回匹配位置
}
int match(string s,string p) {
int lens = s.length(), lenp = p.length(), i = 0, j = 0;
for (i = 0; i < lens - lenp + 1; i++){
for (j = 0; j < lenp; j++)
if (p[i + j] != s[j])
break;
if(j >= lenp)
break; //找到匹配子串
}
return i;
}
暴力的时间复杂度是
KMP算法
构思
观察一种特殊的情况,可以让暴力的时间复杂度为
S: 000000000……0000001
P: 0001
我们发现,造成复杂度太大的原因是因为大量的局部匹配,每一轮的

只要局部匹配很多,效率必将很低
实际上,重复的对比操作没有必要,既然我们已经掌握了
回到刚才那个例子,观察一次迭代中失败的那次对比,尽管这次对比是失败的,但意味着,我们在此之前已经获得了足够多次成功的匹配,在这个例子中,也就是
.. |0|0 0 0 0 0|0 0 0 ...
|0|0 0 0 0 0|1
| |0 0 0 0 0|0 1
对于如上情况,我们可以发现,即便下一轮迭代只能将模式串整体右移一个字符,但相较于暴力算法,中间那五个连续的
如此一来,
- 比对成功,则与
同步前进一个字符 - 否则,
更新为某个更小的 ,并继续比对
再一个更为复杂的情况,考察如下的文本串和模式串

这一轮的迭代,首次失配于
next 表
一般地,如果前一轮对比终止于

由图可见,经过此前一轮的比对,已经可以确定匹配的范围应为:
于是,若模式串
通过上面这个式子表示了,
这个式子表示了,
显然,一个集合中可能包含多个这样的
从图中可以看出下一轮的对比从
一旦发现
既然集合
实现
int match(string s,string p) {
vector<int> nxt = build_next(p);
int len_s = s.length(),len_p = p.length();
int i = 0,j = 0;
while(j < len_p && i < len_s) {
if (0 > j || s[i] == p[j]) // 若匹配,或 p 已移出最左侧(两个判断的次序不可交换)
i += 1, j += 1;
else
j= nxt[j];
}
return i - j;
}
提示:若使用万能头且声明形如
vector<int> next的数组,则数组名称不能使用next,会与stl_iterator_base_funcs.h中的保留关键字next冲突.
构造next表
不难看出,只要
那么
那么,已知

若
如何理解?

以左边的红线为界,可以发现,下面的
所以一般地,若

有点类似于
由于
vector<int> build_next(string p){
int len_p=p.length(),j=0;
vector<int> nxt(len_p);
int t=nxt[0]=-1;
while(j<len_p-1){
if(0>t||p[j]==p[t])
j++,t++,nxt[j]=t;
else
t=nxt[t];
}
return nxt;
}
#include <bits/stdc++.h>
using namespace std;
vector<int> build_next(string p) {
int len = p.size(), j = 0;
vector<int> nxt(len + 1, 0);
int t = nxt[0] = -1;
while (j < len) {
if (0 > t || p[j] == p[t])
j += 1, t += 1, nxt[j] = t;
else
t = nxt[t];
}
return nxt;
}
vector<int> kmp(string s, string p) {
auto nxt = build_next(p);
vector<int> ans;
int i = 0, j = 0;
while (i < s.size() && j < int(p.size())) {
if (0 > j || s[i] == p[j]) {
i += 1, j += 1;
if (j == p.size()) {
ans.push_back(i - j + 1);
j = nxt[j];
}
}
else
j = nxt[j];
}
return ans;
}
int main() {
string s, p; cin >> s >> p;
auto ans = kmp(s, p);
for (auto x : ans)
cout << x << "\n";
vector<int> nxt = build_next(p);
for (int i = 1; i < nxt.size(); i++)
cout << nxt[i] << " ";
cout << '\n';
return 0;
}