1. 程式人生 > >Codeforces 528 D. Fuzzy Search

Codeforces 528 D. Fuzzy Search

題目描述

題解:

這題是字串匹配的加強版。
我們可以先預處理出S串的每一個位置能放那些字母。
然後我們考慮對於每一種字母分開來處理。
假設處理字母k。
對於S中的每一位,有可以放這個字母k和不能放兩種情況。
對於T中的每一位,有是k和不是k兩種情況。
那麼對於這個字母,如果S和T的某一位不能匹配只有一種情況:S沒有k,而T有k。
我們考慮用FFT來解決字串的匹配問題。
那麼我們可以考慮如果S的某一位有k,就賦0,否則賦1;T是k,就賦1,否則賦0。
假設S,T分別長n和m,這樣我們求的就是:

i = 1 m S x +
i
T i \sum_{i=1}^mS_{x+i}T_i
那麼我們可以倒一倒T,就變成了卷積的形式。直接FFT四次即可。

程式碼如下:

#include<bits/stdc++.h>
using namespace std; const int maxn=200005; int n,m,k,hsh[5],S[maxn],T[maxn],ans[maxn][4]; bool pd[maxn][4]; inline int read(){ char ch=getchar(); while (ch!='A'&&ch!='C'&&ch!='G'&&ch!='T') ch=getchar(); if (ch=='A') return 0; else if (ch=='C') return 1; else if (ch=='G') return 2; else return 3; } void prework(){ int l=0,r=-1; for (int i=0;i<=n;i++){ while (l<i-k) hsh[S[l]]--,l++; while (r<i+k&&r<n) r++,hsh[S[r]]++; for (int j=0;j<4;j++) if (hsh[j]) pd[i][j]=1; else pd[i][j]=0; } } namespace FFT{ const int maxm=(1<<19)+5; const double Pi=acos(-1.0); int R[maxm],limn,p[maxm]; struct comx{ double x,y; comx(double xx=0,double yy=0) {x=xx,y=yy;} comx operator +(const comx b){return comx(x+b.x,y+b.y);} comx operator -(const comx b){return comx(x-b.x,y-b.y);} comx operator *(const comx b){return comx(x*b.x-y*b.y,x*b.y+y*b.x);} }a[maxm],b[maxm],w[maxm]; void fft(comx *a,int lim){ for (int i=0;i<lim;i++) if (R[i]>i) swap(a[i],a[R[i]]); for (int t=lim>>1,d=1;d<lim;d<<=1,t>>=1) for (int i=0;i<lim;i+=(d<<1)) for (int j=0;j<d;j++){ comx p=w[t*j]*a[i+j+d]; a[i+j+d]=a[i+j]-p,a[i+j]=a[i+j]+p; } } void pre(int x){ int L=0; limn=1; while (limn<=n+m) limn<<=1,L++; for (int i=0;i<limn;i++){ a[i].x=a[i].y=b[i].x=b[i].y=0.0; R[i]=((R[i>>1]>>1)|((i&1)<<(L-1))); w[i]=comx(cos(2.0*Pi/limn*i),sin(2.0*Pi/limn*i)); } for (int i=0;i<=n;i++) if (pd[i][x]==1) a[i].x=0; else a[i].x=1; for (int i=0;i<=m;i++) if (T[i]==x) b[m-i].x=1; else b[m-i].x=0; } void solve(int x){ pre(x); fft(a,limn); fft(b,limn); for (int i=0;i<limn;i++) a[i]=a[i]*b[i],w[i].y=-w[i].y; fft(a,limn); for (int i=0;i<limn;i++) w[i].y=-w[i].y,a[i].x/=limn; memset(p,0,sizeof(p)); for (int i=0;i<=n+m+1;i++) p[i]=(int)(a[i].x+0.5); for (int i=m;i<=n;i++) if (p[i]==0) ans[i][x]=0; else ans[i][x]=1; } } int main(){ scanf("%d%d%d",&n,&m,&k); for (int i=0;i<n;i++) S[i]=read(); for (int i=0;i<m;i++) T[i]=read(); n--,m--; prework(); for (int i=0;i<4;i++) FFT::solve(i); int sum=0; for (int i=m;i<=n;i++) if (ans[i][0]==0&&ans[i][1]==0&&ans[i][2]==0&&ans[i][3]==0) sum++; printf("%d\n",sum); return 0; }