【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;
}