【模板】 KMP字符串
传送门
题意
给定一个模式串 \(S\),以及一个模板串 \(P\),模板串 \(P\) 在模式串 \(S\) 中多次作为子串出现。
求出模板串 \(P\) 在模式串 \(S\) 中所有出现的位置的起始下标。
数据范围
\(1\leq N \leq 10^{5}\)
\(1\leq M \leq 10^{6}\)
题解
求出模板串 \(P\) 的 \(next\) 数组即前缀函数,然后枚举匹配的起点,起点需要在进行匹配,
如果失败就从当前开头与这个前缀匹配的点重新开始匹配即可
- 引理:对于一个可行的 \(j_{0}\),\(<j_{0}\) 的最大可行点为 \(ne[j_{0}]\)
- 在 \(while\) 循环中,\(j\) 的值不断减小,\(j=ne[j]\) 的执行次数不会超过每一次 \(for\) 循环开始时候 \(j\) 与结束时 \(j\) 的值之差
- 执行次数最多的情况即当前所有的 \(ne[j]\) 的值相差都是 \(1\)
- \(j\) 减小的幅度总和不会超过 \(j\) 增加的幅度总和,故 \(j\) 的总变化次数最多为 \(2(N+M)\),复杂度为 \(O(N+M)\)
Code
#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,n) for(int i=a;i<n;i++)
#define ll long long
const int N=1e6+10;
int ne[N];
int n,m;
char s[N],p[N];
int main(){
cin>>n>>p+1>>m>>s+1;
int j=0;
rep(i,2,n+1){
while(j && p[i]!=p[j+1]) j=ne[j];
if(p[i]==p[j+1]) j++;
ne[i]=j;
}
j=0;
rep(i,1,m+1){
while(j && s[i]!=p[j+1]) j=ne[j];
if(s[i]==p[j+1]) j++;
if(j==n) cout<<i-n<<' ',j=ne[j];
}
}

浙公网安备 33010602011771号