【CF954I】Yet Another String Matching Problem(FFT)

时间:2022-12-22 10:17:32

【CF954I】Yet Another String Matching Problem(FFT)

题面

给定两个字符串\(S,T\)

\(S\)所有长度为\(|T|\)的子串与\(T\)的距离

两个等长的串的距离定义为最少的,将某一个字符全部视作另外一个字符的次数。

\(|T|<=|S|<=10^6\),字符集大小为\(6\)

题解

考虑如何快速计算两个串的答案,从左向右扫一遍,如果对应位置上有两个字符不同,检查在并查集中是否属于同一个集合,如果不属于则答案加一,同时合并两个集合。(这个就是CF939D)

如果枚举每一个长度为\(|T|\)的子串,复杂度为\(O(|S||T|)\)。考虑优化。

\(T\)串反转,枚举两个字符\(x,y\),将\(S\)串的\(x\)字符出现的位置对应为\(1\),\(T\)串的\(y\)字符出现的位置对应为\(1\),其他对应为\(0\),然后求两个生成函数的卷积。

假设在\(T\)\(a\)位置和\(S\)\(b\)位置对应有\(1\),那么它们会对\(a+b\)位置对应一个\(1\),也就是\(b+|T|-a\)位置对应一个\(1\),同时意味着在\(S\)的从这个位置开始的长度为\(|T|\)的子串中,这个位置上对应着这两个字符。

于是枚举每个开始的位置,以及任意两个字符,如果在任意位置这两个字符有对应相等的话,并查集合并即可。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define ll long long
#define RG register
#define MAX 333333
const double Pi=acos(-1);
struct Complex{double a,b;}A[MAX],B[MAX],W[MAX];
Complex operator+(Complex a,Complex b){return (Complex){a.a+b.a,a.b+b.b};}
Complex operator-(Complex a,Complex b){return (Complex){a.a-b.a,a.b-b.b};}
Complex operator*(Complex a,Complex b){return (Complex){a.a*b.a-a.b*b.b,a.a*b.b+a.b*b.a};}
int r[MAX],N,n,m,l,eql[MAX][6][6];
char a[MAX],b[MAX];
void FFT(Complex *P,int opt)
{
    for(int i=1;i<N;++i)if(i<r[i])swap(P[i],P[r[i]]);
    for(int i=1;i<N;i<<=1)
        for(int p=i<<1,j=0;j<N;j+=p)
            for(int k=0;k<i;++k)
            {
                Complex w=(Complex){W[N/i*k].a,W[N/i*k].b*opt};
                Complex X=P[j+k],Y=w*P[i+j+k];
                P[j+k]=X+Y;P[i+j+k]=X-Y;
            }
}
int f[6];
int getf(int x){return x==f[x]?x:f[x]=getf(f[x]);}
int main()
{
    scanf("%s",a);scanf("%s",b);
    n=strlen(a),m=strlen(b);
    for(N=1;N<=(n+m);N<<=1)++l;
    for(int i=0;i<N;++i)r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
    for(int i=1;i<N;i<<=1)
        for(int k=0;k<i;++k)W[N/i*k]=(Complex){cos(k*Pi/i),sin(k*Pi/i)};
    for(int i=0;i<6;++i)
        for(int j=0;j<6;++j)
        {
            for(int k=0;k<N;++k)A[k].a=A[k].b=B[k].a=B[k].b=0;
            for(int k=0;k<n;++k)A[k].a=(a[k]==i+97);
            for(int k=0;k<m;++k)B[k].a=(b[m-k-1]==j+97);
            FFT(A,1);FFT(B,1);
            for(int k=0;k<N;++k)A[k]=A[k]*B[k];
            FFT(A,-1);
            for(int k=0;k<N;++k)eql[k][i][j]=(int)(A[k].a/N+0.5);
        }
    for(int i=m-1;i<n;++i)
    {
        for(int j=0;j<6;++j)f[j]=j;
        for(int j=0;j<6;++j)
            for(int k=0;k<6;++k)
                if(eql[i][j][k])
                    f[getf(j)]=getf(k);
        int ans=0;
        for(int j=0;j<6;++j)if(getf(j)!=j)++ans;
        printf("%d ",ans);
    }
    puts("");
    return 0;
}