一些理解
关于模式串的跳转
首先,对于模式串求出一个 next \text{next} next 数组, next i \text{next}_i nexti 表示模式串的前 i i i 个字符中真前缀和真后缀的最大相等的长度。
然后我们在匹配的时候如果在 i i i 这个位置适配了(也就是 S i ≠ t i S_i \ne t_i Si=ti):
那么我们就需要找到 next i \text{next}_i nexti,然后把模式串的 next i \text{next}_i nexti 的位置跳到 i i i 的位置上去(红色的这两部分是相同的):
然后就又可以继续向下匹配了。
关于怎么求 next \text{next} next
现在已经求出了前 i i i 个的 next \text{next} next:
如果 t [ n e x t [ i ] + 1 ] = t [ i + 1 ] t[next[i] + 1] = t[i + 1] t[next[i]+1]=t[i+1],那么继续往后扫,知道不相等为止就是 i + 1 i + 1 i+1 的 next \text{next} next。
如果不等,那么就继续跳前面 next i \text{next}_i nexti 的 next \text{next} next,也就是这样:
又因为红色部分是相同的,所以四个蓝色的部分都是相同的,然后就继续向后扩展,不能扩展之后就继续跳 next \text{next} next。
代码
#include<bits/stdc++.h>
using namespace std;
#define MAXN 1001000
int n = 0, m = 0;
char s[MAXN], t[MAXN];
int f[MAXN] = { 0 };
int nxt[MAXN] = { 0 };
void kmp(){
nxt[1] = 0;
for(int i = 2, j = 0; i <= n; i++){
while(j > 0 and t[j + 1] != t[i]) j = nxt[j];
if(t[j + 1] == t[i]) j++;
nxt[i] = j;
}
for(int i = 1, j = 0; i <= m; i++){
while(j > 0 and t[j + 1] != s[i]) j = nxt[j];
if(t[j + 1] == s[i]) j++;
f[i] = j;
}
}
int main(){
cin >> (s + 1) >> (t + 1);
n = strlen(t + 1), m = strlen(s + 1);
kmp();
for(int i = 1; i <= m; i++)
if(f[i] == n) cout << i - n + 1 << '\n';
for(int i = 1; i <= n; i++)
cout << nxt[i] << ' '; puts("");
return 0;
}