【KMP统计】 匹配统计

传送门

题意

给定字符串\(A,B\),给出\(Q\)个询问,每次询问给定一个\(x\),求满足\(A\)从当前位置开始的后缀子串与模板串\(B\)匹配的长度刚好是\(x\)
求在整个字符串中这样的位置数量是多少

数据范围

\(1 \leq N, M, Q, x \leq 200000\)

题解

二分做法:

  • 枚举所有起点的\(i\)后缀,二分能够匹配的最大长度,每次只需要匹配当前点\(i\sim i+M\)的即可

  • 将字符串通过\(hash\)预处理,判断\(hash\)值是否相等来判断两个字符串是否相等

  • 维护\(cnt\)\(cnt[i]\)表示匹配长度为\(i\)下的后缀个数

  • 预处理\(A、B\)\(hash\)值为\(O(N+M)\),二分\(N\)次,每次\(logM\),即\(O(NlogM)\)\(Q\)个查询,每次\(O(1)\)

    • 总的时间复杂度为\(O(NlogM + Q +M)\)

\(kmp\)做法:

  • \(kmp\)过程中只能求出来以\(j\)为终点的待匹配串匹配的长度

    • 只要\(j\)匹配,\(next[j]\)就一定匹配,\(next[next[j]]\)也一定匹配,直至为\(0\)
  • 维护一个\(cnt\)\(cnt_i\) 表示以\(i\)为终点的与\(B\)匹配长度\(\geq i\)的后缀个数

  • \(cnt[ne[i]]\)需要累积\(cnt[i]\)的值,如果在求的过程中累加复杂度会退化为\(O(N·M)\)

    • 在求出所有当前的\(cnt[i]\)后,从\(M\)开始累计即可
  • \(cnt[i]-cnt[i+1]\)就是长度为\(i\)的后缀数量

Code

#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,n) for(int i=a;i<n;i++)
#define per(i,a,n) for(int i=n-1;i>=a;i--)
#define ll long long
#define ull unsigned long long 

const int N=2e5+10;
int ne[N],cnt[N];
int n,m,q;
char A[N],B[N];
void kmp()
{
    int j=0;
    rep(i,2,m+1)
    {
        while(j && B[i] != B[j+1]) j=ne[j];
        if(B[i]==B[j+1]) j++;
        ne[i]=j;
    }
    j=0;

    rep(i,1,n+1)
    {
        while(j && A[i]!=B[j+1]) j=ne[j];
        if(A[i]==B[j+1]) j++;
        cnt[j]++;
    }
    per(i,1,m+1) cnt[ne[i]]+=cnt[i];
}
void solve()
{  
    cin>>n>>m>>q;
    cin>>A+1>>B+1;

    int x;
    kmp();
    while(q--)
    {
        cin>>x;
        cout<<cnt[x]-cnt[x+1]<<endl;
    }
}
int main()
{
    solve();
}

posted @ 2020-09-29 09:59  Hyx'  阅读(166)  评论(0)    收藏  举报