两个串-----FFT妙用!

题面描述:S、T两个串,求出T在S中出现了几次。其中T中有'?'可以匹配任意字符。S长度n不超过10^5,T长度m不超过S长度。


乍一看因该是个关于字符串处理的题目,然后我就杯具了。。。。

果断滚粗去看题解,这居然是到FFT!

先把T字符串翻转,定义 c[j+m-1] = sigma(  (a[j+i]-b[m-1-i])^2  *  a[j+i]  *  b[m-1-i]  )(0<=i<m)

其中定义 字符’?‘的值为0,'a'~’z'为1~26,a为S的权值数组,b为T的权值数组。

那么如果c[j+m-1]=0,那么S[j~j+m-1]与T匹配,否则不匹配。

直接求c数组复杂度是O(nm),如何快速求c数组?


将式子展开得  c[j+m-1] = sigma(  a[j+i]^3*b[m-1-i]  )  +  sigma(  a[j+i]*b[m-1-i]^3 )  -  2*sigma(  a[j+i]^2*b[m-1-i]^2 )

每一个可以发现这其实是个卷积,对于每个Sigma里用FFT求一下即可,复杂度O(nlogn)

(蒟蒻被虐得爽爽的敲打


//吐槽:stl里的complex确实好慢。。。老老实实的手写去了委屈

#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
const int Maxn=400005;
#define sqr(a)  ((a)*(a))
#define cube(a) ((a)*(a)*(a))
typedef long long LL;
int n,m,N,M,i,j,k,cnt,rev[Maxn],dig[20];
LL ans[Maxn];
char S[Maxn],T[Maxn];
struct CP
{
  double X,Y;
  CP operator +(const CP &a){ return (CP){X+a.X,Y+a.Y}; }
  CP operator -(const CP &a){ return (CP){X-a.X,Y-a.Y}; }
  CP operator *(const CP &a){ return (CP){X*a.X-Y*a.Y,X*a.Y+Y*a.X}; }
} a[Maxn],b[Maxn];

void FFT(CP a[],int flag){
  for (i=0;i<N;i++)
    if (i<rev[i]) swap(a[i],a[rev[i]]);
  for (i=2;i<=N;i<<=1){
  	CP wn = (CP) { cos(2*M_PI/i), flag*sin(2*M_PI/i) };
  	for (j=0;j<N;j+=i){
  	  CP w = (CP) {1,0};
  	  for (k=j;k<j+i/2;k++){
  	  	CP x=a[k], y=a[k+i/2]*w;
  	  	a[k]=x+y; a[k+i/2]=x-y; w=w*wn;
  	  }
  	}
  }
  if (flag==1) return;
  for (i=0;i<N;i++) a[i].X/=N;
}

void calc(LL flag){
  FFT(a,1); FFT(b,1);
  for (i=0;i<N;i++) a[i]=a[i]*b[i];
  FFT(a,-1);
  for (i=m-1;i<m-1+n;i++)
    ans[i-m+1]+=flag * (LL) (a[i].X+0.5);
}

int main(){
  freopen("match.in","r",stdin);
  freopen("match.out","w",stdout);
  scanf("%s",S); n=strlen(S);
  scanf("%s",T); m=strlen(T);
  for (i=0;i+i<m-1;i++)
    swap(T[i],T[m-1-i]);
  
  for (N=2, M=1;N<(n+m);N<<=1, M++);
  for (i=0;i<N;i++){
  	int len=0;
  	for (j=i;j>0;j>>=1) dig[len++]=(j&1);
  	for (j=0;j<M;j++) rev[i]=( (rev[i]<<1)|dig[j] );
  }
  
  // a^3*b
  for (i=0;i<n;i++)
    {a[i]=(CP){S[i]!='?'?S[i]-'a'+1:0, 0}; a[i].X=cube(a[i].X);}
  for (i=n;i<N;i++) a[i]=(CP){0,0};
  for (i=0;i<m;i++) b[i]=(CP){ T[i]!='?'?T[i]-'a'+1:0, 0};
  for (i=m;i<N;i++) b[i]=(CP){0,0};
  calc(1);
  
  //a*b^3
  for (i=0;i<n;i++) a[i]=(CP){S[i]!='?'?S[i]-'a'+1:0, 0};;
  for (i=n;i<N;i++) a[i]=(CP){0,0};
  for (i=0;i<m;i++)
    {b[i]=(CP){ T[i]!='?'?T[i]-'a'+1:0, 0}; b[i].X=cube(b[i].X);}
  for (i=m;i<N;i++) b[i]=(CP){0,0};
  calc(1);
  
  //a^2*b^2
  for (i=0;i<n;i++)
    {a[i]=(CP){S[i]!='?'?S[i]-'a'+1:0, 0};; a[i].X=sqr(a[i].X);}
  for (i=n;i<N;i++) a[i]=(CP){0,0};
  for (i=0;i<m;i++)
    {b[i]=(CP){ T[i]!='?'?T[i]-'a'+1:0, 0}; b[i].X=sqr(b[i].X);}
  for (i=m;i<N;i++) b[i]=(CP){0,0};
  calc(-2);
  
  for (i=0;i<=n-m;i++)
    if (ans[i]==0) cnt++;
  printf("%d\n",cnt);
  for (i=0;i<=n-m;i++)
    if (ans[i]==0) printf("%d\n",i);
  
  return 0;
}


你可能感兴趣的:(fft)