高速在一个字符串中查找子串。时间复杂度只需要 O(n)。n 是字符串的长度。

next[i] = j 的含义:在模式串 p 中,以 i 结尾的,满足最长前缀和后缀关系,前缀的最后一个位置为 j。即 P[1 ~ j] = P[i - j + 1, i] 相等。

s 串和 p 串都从下标 1 开始存。

模式串中的指针不用每次匹配失败时都退到 0。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <iostream>
using namespace std;
const int N = 1e5 + 10;
const int M = 1e6 + 10;

int n, m;
char p[N], s[M];
int ne[N];

int main() {
cin >> n >> p + 1 >> m >> s + 1;
// 求 next 数组的过程,模式串自己跟自己比较,过程中记录 next 数组
for (int i = 2, j = 0; i <= n; i++) {
while (j && p[i] != p[j + 1]) j = ne[j];
if (p[i] == p[j + 1]) j++;
ne[i] = j;
}
for (int i = 1, j = 0; i <= m; i++) {
// 如果当前位置字符不匹配,j 需要往后退。如果 j 退无可退已经是
// 0,就不用执行这句了
while (j && s[i] != p[j + 1]) j = ne[j];
// 如果 j 经过后退,模式串 j 位置的字符能够匹配上当前字符
if (s[i] == p[j + 1]) j++;
if (j == n) {
// 如果模式串已经匹配成功,i 是 s 串中最后一个匹配成功的位置下标
printf("%d ", i - n);
// j 需要退一次,开始寻找下一个匹配
j = ne[j];
}
}
return 0;
}