CF528D Fuzzy Search

https://www.luogu.com.cn/problem/CF528D

字符串匹配\(/NTT\)

如果\(k=0\),那么\(KMP\)轻松解决

\(k>0\)时,我们可以对每个字符分别考虑

例如样例,用\('A'\)去匹配:

\[AGCAATTCAT\\ ACAT \]

将模式串和文本串中非\('A'\)字符当成\('*'\)

我们模拟样例进行操作:

\[A**AA***A*\\ A*A*\\ \because k=1\\ 我们可以让文本串中的A拓展(将周围k范围内的字符全部变成A)\\ 这可以用差分O(n)解决\\ 那么文本串变为:AAAAAA*AAA\\ 令A的值为1,非A字符值为0\\ 如果模式串中含有q个A,要在i位置匹配,必然满足:\\ \sum_{j=1}^m S_{i+j-1}\times T_{j} = q\\ S与T下标差一致,与多项式类似,但是不能直接NTT,\\ 我们把T倒过来就好了!\\ \sum_{j=1}^m S_{i+j-1}\times T_{m-j+1}^{reverse} = q\\ (i+j-1)+(m-j+1)=i+m\\ NTT解决! \]

那么\(4\)个字符呢?

很简单,每一个字符都必须能够在同一位置匹配,才能得出该位置是合法的

代码很好写

\(C++ Code:\)

#include<iostream>
#include<cstdio>
#include<algorithm>
#define N 200005
#define p 998244353
#define ll long long
using namespace std;
char s1[N],s2[N];
int s,n,m,k,ans[N],rev[N << 2],Ans,w[N];
ll a[N << 2],b[N << 2],G[2][24];
ll ksm(ll x,ll y)
{
    ll ans=1;
    while (y)
    {
        if (y & 1)
            ans=ans*x%p;
        x=x*x%p;
        y >>=1;
    }
    return ans;
}
#define inv(x) (ksm(x,p-2))
void NTT(ll *a,int t)
{
    for (int i=0;i<s;i++)
        if (i<rev[i])
            swap(a[i],a[rev[i]]);
    for (int mid=1,o=1;mid<s;mid <<=1,o++)
        for (int j=0;j<s;j+=(mid << 1))
        {
            ll g=1;
            for (int k=0;k<mid;k++,g=g*G[t][o]%p)
            {
                ll x=a[j+k],y=g*a[j+k+mid]%p;
                a[j+k]=(x+y)%p;
                a[j+k+mid]=(x-y)%p;
            }
        }
}
void test(char c)
{
    int q=0;
    for (int i=0;i<m;i++)
        if (s2[i]==c)
            b[i]=1,q++; else
            b[i]=0;  
    for (int i=0;i<=n;i++)
        w[i]=0;
    for (int i=0;i<n;i++)
        if (s1[i]==c)
            w[max(i-k,0)]++,w[min(i+k,n-1)+1]--;
    s=0;
    for (int i=0;i<n;i++)
    {
        s+=w[i];
        if (s>0)
            a[i]=1; else
            a[i]=0;  
    }
    int l=0;
    s=1;
    while (s<n+m)
    {
        l++;
        s <<=1;
    }
    for (int i=n;i<s;i++)
        a[i]=0;
    for (int i=m;i<s;i++)
        b[i]=0;
    for (int i=0;i<s;i++)
        rev[i]=(rev[i >> 1] >> 1) | ((i & 1) << (l - 1));
    NTT(a,0);
    NTT(b,0);
    for (int i=0;i<s;i++)
        a[i]=a[i]*b[i]%p;
    NTT(a,1);
    ll t=inv(s);
    for (int i=0;i<s;i++)
        a[i]=a[i]*t%p,a[i]=(a[i]%p+p)%p;
    for (int i=0;i<s;i++)
        if (a[i]==q&&i>=m-1&&i<n)
            ans[i]++;
}
void Pre()
{
    G[0][23]=ksm(3,(p-1)/(1 << 23));
    G[1][23]=inv(G[0][23]);
    for (int i=22;i>=1;i--)
    {
        G[0][i]=G[0][i+1]*G[0][i+1]%p;
        G[1][i]=G[1][i+1]*G[1][i+1]%p;
    }
}
int main()
{
    Pre();
    scanf("%d%d%d",&n,&m,&k);
    scanf("%s%s",s1,s2);
    for (int i=0;i<=m/2-1;i++)
        swap(s2[i],s2[m-i-1]);
    test('A');
    test('G');
    test('C');
    test('T');
    for (int i=0;i<n;i++)
        if (ans[i]==4)
            Ans++;
    printf("%d\n",Ans);
    return 0;
}
posted @ 2020-08-01 12:38  GK0328  阅读(150)  评论(0编辑  收藏  举报