KMP算法
一个人能走的多远不在于他在顺境时能走的多快,而在于他在逆境时多久能找到曾经的自己。
--- KMP
给定一个字符串S 以及一个模式串P
模式串P在字符串S中多次作为子串出现
求出模式串出现的下标
关键在于匹配不成功时不回退字符串S而是改变模式串继续匹配的位置来避免冗余操作。

我们通过这个例子先感受一下是next是什么
首先我们要知道前缀与后缀的概念
对于"aba"
前缀有 a ab aba
后缀有 a ba aba
对于S and P 两个字符串 如果在下标i处出现差异 这就意味着在1 到 i-1处是相同的
如果我们找到了最大的相等的前缀与后缀 我们就不必重新开始匹配而是借用已经匹配的有效片段继续进行
i 1 2 3 4 5 6
S a b a b a c
P a b a b a d
j 1 2 3 4 5 6
在下标6处不匹配 我们发现 在 1 - 5 a b a b a 最大相等的前缀与后缀是 a b a
那么接下来j 回退到3 再继续比较 i 则不用回退 仍然指向6 再比较Si Pj+1
我们可以这么想 好似平移 因为前缀与后缀相同 相当于是把P从前缀移动到了后缀再继续去比较(好吧确实有点抽象)
i
S a b a b a c
p a b a b a d
j
就类似这样 再去比较Si pJ+1 如果相同J++ 继续往后比 不同就再找上一个最大的前后缀
求next的做法与匹配过程相似 由于只有1个的时候的ne一定为0 我们从i = 2 开始 j = 0 因为我们每次比较的都Pi 和Pj+1
这个就相当于是拿前段 和后段去比较对比了 如果PI != PJ+1 j = nej 回到上一个前后缀相同的地方
1 2 3 4 5 6
a b a b a c
发现p6 != p3+1 j = nej = ne3 = 1
这样做的原因是我们已经知道了这个最大的前后缀已经没办法用了 因为下一个字母不再匹配 而我们的任务是要获得新的最大的前后缀 因此回退到上一个前后缀相同的地方 在这道题就是 a b a 这个相同的前后缀已经无法使用 我们只能回退到a这个前后缀 并判断PJ+1 是否与新加的字母相同,如果不相同 继续回退,最后J就是匹配的字母的数量 也就是最大的前后缀。
c++
#include <iostream>
#include <string>
using namespace std;
const int N = 100010;
char s[N] , p[N];
int ne[N];
int main(){
int n , m;
cin >> n >> p+1 >> m >> s+1;
//求ne数组
for(int i = 2 , j = 0 ; i <= n ; i++){
while(j && p[i] != p[j]) j = ne[j];
if(p[i] == p[j+1]) j++;
ne[i] = j;
}
//匹配KMP
for(int i = 1 , j = 0 ; i <= n ; i++){
while(j && s[i] != p[j+1]) j = ne[j];
if(s[i] == p[j+1]) j++;
if(j == n){
cout << i - n << endl;
j = ne[j] //找其他符合题意得
}
}
return 0;
}
j == n){
cout << i - n << endl;
j = nej //找其他符合题意得
}
}
return 0;
}