【模板】 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];
    }
}

posted @ 2020-09-28 16:50  Hyx'  阅读(111)  评论(0)    收藏  举报