蠻經典的 FFT 題。(以下使用 0 base )因為只要注意到, $T$ 不能放在 $i$ 位置的充要條件是:存在 $j$ 滿足 $S[i+j]$ 的前後 $k$ 個之中沒有字母 $T[ j ]$ 。因此首先把每個字母分開考慮,對於每個字母 $c$ 可以先 $O( |S| )$ 處理出「哪些位置的前後 $k$ 個之中沒有 $c$ 」,這只要先算出「前 $i$ 個字母中有幾個 $c$ 」就可以 $ O( 1 ) $ 知道一個區間裡是否有 $c$ 了。假設所有上面所說的位置為 $a_1$ ~ $a_k$ ,還有 $T$ 中的所有 $c$ 的出現位置 $b_1$ ~ $b_r$ ,那麼考慮一個多項式 $f( x ) = x^{a_1} + x^{a_2} + ... + x^{a_k}$ ,還有$ g( x ) = x^{ m-1-b_1 } + x^{ m-1-b_2 } + ... + x^{ m-1-b_r } $(其中 $m = | T |$),那麼如果 $f( x ) * g( x )$ 的 $x^i$ 係數不為 $0$ 的話,就代表把 $T$ 放在 $i - m + 1$ 位置時是不合法的。因此只要作四次多項式乘法,把他全部加起來,然後去看冪次 $m-1$ ~ $n-1$ 中有幾個係數為$0$的項就可以了。
code :
#include<bits/stdc++.h> #define DB double #define comp complex<DB> using namespace std; const int maxn=1<<19 ; const DB PI=2*acos(0.0) ; comp tmp[maxn] ; void DFT(comp *a,int n,int idft) { if(n==1) return ; for(int i=0;i<n;i++) tmp[i]=a[i] ; for(int i=0;i<n;i++) a[i%2 ? n/2+i/2 : i/2]=tmp[i] ; comp *a1=a , *a2=a+n/2 ; DFT(a1,n/2,idft) ; DFT(a2,n/2,idft) ; comp w(cos(2*PI/n),sin(2*PI/n)) , now(1,0) ; if(idft) w=conj(w) ; for(int i=0;i<n/2;i++,now*=w) { tmp[i]=a1[i]+now*a2[i] ; tmp[i+n/2]=a1[i]-now*a2[i] ; } for(int i=0;i<n;i++) a[i]=tmp[i] ; } void mul(comp *a,comp *b,comp *c,int n) { DFT(a,n,0) ; DFT(b,n,0) ; for(int i=0;i<n;i++) c[i]=a[i]*b[i] ; DFT(c,n,1) ; for(int i=0;i<n;i++) c[i]/=n ; } comp a[maxn],b[maxn],ans[maxn] ; char s[maxn],t[maxn] ; int sum[maxn],n,m,k ; void solve(char ch) { for(int i=0;i<maxn;i++) a[i]=b[i]=(comp){0,0} ; for(int i=0;i<m;i++) if(t[i]==ch) b[m-1-i]=(comp){1,0} ; for(int i=0;i<n;i++) sum[i+1]=sum[i]+(s[i]==ch) ; for(int i=0;i<n;i++) if(sum[min(i+k,n-1)+1]==sum[max(i-k,0)]) a[i]=(comp){1,0} ; mul(a,b,a,maxn) ; for(int i=0;i<maxn;i++) ans[i]+=a[i] ; } main() { scanf("%d%d%d%s%s",&n,&m,&k,s,t) ; for(int i=0;i<4;i++) solve("ATCG"[i]) ; int cnt=0 ; for(int i=m-1;i<=n-1;i++) if(int(real(ans[i])+0.5)==0) cnt++ ; printf("%d\n",cnt) ; }
沒有留言:
張貼留言