一种十分优秀的字符串匹配算法,时间复杂度为线性
首先定义如下变量:
a - 文本串
b - 模式串
我们又定义如下变量:
i - 指向文本串a的指针
j - 指向模式串b的指针
nxt - nxt[j]表示串b[0: j]中既是前缀也是后缀(不包括b[0: j]自身)的字符串的最大长度
在a[i] != b[j]时,当前位置发生匹配失败,此时我们应该想到回退j,使得b[0: j]这样的最长前缀能够匹配到a[i - j: i],然后继续之后的匹配,假设此时nxt[j - 1] = x,则有b[0:x] == b[j - 1 - x: j - 1] == a[i - 1 - x: i - 1],所以j回退到nxt[j - 1]就是最好的选择
nxt数组求法:可以用串b[0: ]对串b[1: ]进行匹配求出,令nxt[0] = 0,此为 j 能回退到的最小位置。在自我匹配过程中,若是匹配失败,则 j 需要回退,可直接令 j = nxt[j - 1],因为 nxt[j - 1] 在遍历到 j 之前就已经求过,若是匹配成功,则令j++,nxt[j] = j即可
题目链接: 子串查找
#include <bits/stdc++.h>
using namespace std;
int nxt[1000005], res = 0;
void get_next(string b) {
nxt[0] = 0;
for (int i = 1, j = 0; i < b.size(); i++) {
while (j && b[i] != b[j]) {
j = nxt[j - 1];
}
if (b[i] == b[j]) {
j++;
}
nxt[i] = j;
}
}
void kmp(string a, string b) {
for (int i = 0, j = 0; i < a.size(); i++) {
while (j && a[i] != b[j]) {
j = nxt[j - 1];
}
if (a[i] == b[j]) {
j++;
}
if (j == b.size()) {
res++;
j = nxt[j - 1];
}
}
}
int main() {
string a, b;
cin >> a >> b;
get_next(b);
kmp(a, b);
cout << res << endl;
return 0;
}