1. 程式人生 > >CodeForces 528D Fuzzy Search(FFT)

CodeForces 528D Fuzzy Search(FFT)

題意:
給給定兩個字串 S,T(字符集大小為4:A ,G ,C ,T),給定閥值 k,
定義T串在S串某位置匹配,當且僅當T串任意位置的這個字元所對應的母串的位置的左右k個字元之內至少有一個與這個字元相同的。
求T串在S串中的匹配次數。
例如,對於S=AGCAATTCAT,T=ACAT,k=1,匹配次數為3,如圖所示:

這裡寫圖片描述

資料範圍
1 ≤ |T| ≤ |S| ≤ 200 000,0 ≤ k ≤ 200 000

題解:

和上一題相似,把“匹配”轉化為卷積的形式。
但是,有一個問題在於匹配的定義,有閥值這個東西讓我們不能直接構造出合適的表示方法。
要使匹配是準確的,唯獨是一個位置可以表示左右k個位置的字元種類。

那麼,如果我們只考慮一種字元的匹配,可以想到:
對於S串,構造序列f使得當i的左右k位存在該字元時,第i位置為1,否則第i位置為0。
對於T串,構造序列g使得當i位是該字元時,第i位置為1,否則第i位置為0。
那麼對於這個字元的匹配個數就是:
ansi=j=0|T|1fijgj

於是可以四種字元分別跑一遍,加起來,ans為|T|的就匹配上了。

程式碼:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#define LD long double using namespace std; const int N=200005; const int MXN=524288+1000; const long double Pi=acos(-1); char s[N],t[N]; int S,T,k,cnt[5],num[128],len,p,R[MXN],ans[MXN]; struct Virt { long double r,i; Virt(){} Virt(long double r,long double i):r(r),i(i){} Virt operator+(const
Virt &A){return Virt(r+A.r,i+A.i);} Virt operator-(const Virt &A){return Virt(r-A.r,i-A.i);} Virt operator*(const Virt &A){return Virt(r*A.r-i*A.i,r*A.i+i*A.r);} }omg[MXN],_omg[MXN],a[MXN],b[MXN],c[MXN]; bool ok[MXN][5]; void FFT(Virt *x,int opt) { for(int i=0;i<len;i++) if(i<R[i]) swap(x[i],x[R[i]]); Virt *w; if(opt==1) w=omg; else w=_omg; for(int m=2;m<=len;m<<=1) { int l=m>>1; for(int j=0;j<len;j+=m) for(int i=0;i<l;i++) { Virt y=w[len/m*i]*x[i+j+l]; x[i+j+l]=x[i+j]-y; x[i+j]=x[i+j]+y; } } if(opt==-1) for(int i=0;i<len;i++) x[i].r=(LD)x[i].r/(LD)len; } int main() { num['A']=0,num['G']=1,num['C']=2,num['T']=3; scanf("%d%d%d",&S,&T,&k); scanf("%s%s",s,t); cnt[num[s[0]]]=1; for(int i=0,p1=0,p2=0;i<S;i++) { while(p1<i-k){cnt[num[s[p1]]]--; p1++;} while(p2<i+k&&p2<S-1){p2++; cnt[num[s[p2]]]++;} for(int j=0;j<4;j++) if(cnt[j]>0) ok[S-i-1][j]=1; } for(len=1,p=0;len<(S+T);len<<=1,p++); R[0]=0; for(int i=1;i<len;i++) R[i]=(R[i>>1]>>1)|((i&1)<<(p-1)); for(int i=0;i<len;i++) omg[i]=Virt(cos((LD)2*Pi/(LD)len*i),sin((LD)2*Pi/(LD)len*i)),_omg[i]=Virt(omg[i].r,-omg[i].i); for(int c=0;c<4;c++) { for(int i=0;i<len;i++) a[i]=Virt(ok[i][c],0),b[i]=Virt((i<T&&(num[t[i]]==c)),0); FFT(a,1); FFT(b,1); for(int i=0;i<len;i++) a[i]=a[i]*b[i]; FFT(a,-1); for(int i=0;i<len;i++) ans[i]+=(int)(a[i].r+0.5); } int ret=0; for(int i=T-1;i<S;i++) if(ans[i]==T) ret++; printf("%d\n",ret); return 0; }