COGS2216 你猜是不是KMP

第一道自己写的FFT......

不知为啥这题在网上找不到题解......真是麻烦,害得我推了半天......

还是写个简要题解吧......

首先把S和T拆成序列,a~z分别对应成1~26,?是0,设拆成的两个序列分别为A和B。

如果从i开始可以S匹配上T,那么就有

\begin{align}\sum_{j=0}^{m-1}[|A_{i+j}-B_j|=0 \space or \space B_j=0]=m\end{align}

换一个等价写法:

\begin{align}\sum_{j=0}^{m-1}(A_{i+j}-B_j)^2 B_j=0\end{align}

求出所有i对应的值就可以得知每一位是否匹配了。

展开之后发现并没有什么用,这个式子只能$O(n^2)$计算,还不如朴素匹配呢,这玩意儿有个卵用啊(╯‵□′)╯︵┻━┻

尝试把T反转,原式变为

\begin{align}\sum_{j=0}^{m-1}(A_i-B_{m-j-1})^2 B_{m-j-1}\end{align}

展开得

\begin{align}\sum_{j=0}^{m-1}A_{i+j}^2 B_{m-j-1}-2A_{i+j}B_{m-j-1}^2+B_{m-j-1}^3\end{align}

有没有发现这个式子看着有点眼熟......

前两项下标之和都是i+m-1,因此可以看成一个卷积形式,而令最后一项再卷上一个各项全为1的序列(显然不影响结果是吧......),也是卷积,而下标之和是i+m-1,因此定义我们得到的结果为

\begin{align}C_{i+m-1}=\sum_{j=0}^{m-1}A_{i+j}^2 B_{m-j-1}-2A_{i+j}B_{m-j-1}^2+1B_{m-j-1}^3\end{align}

令$D_i=A_i^2,E_i=B_i^2,F_i=B_i^3,I_i=1$,再写成卷积形式就是

\begin{align}C=D*B-2A*E+F*I\end{align}

分别求出三个卷积之后加一下就行了,FFT加速卷积即可,复杂度$O(nlogn)$。

然而常数大如狗,跑的比bitset慢到不知哪儿去了

求出三个卷积的和之后扫一遍判断哪些i对应的$C_{i+m-1}$是0,是的话说明两串在i位置匹配,否则不匹配。

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<cmath>
 4 #include<algorithm>
 5 using namespace std;
 6 const int maxn=270010;
 7 const double pi=acos(-1.0),eps=1e-4;
 8 struct Complex{
 9     double a,b;
10     Complex(double a=0.0,double b=0.0):a(a),b(b){}
11     Complex operator+(const Complex &x)const{return Complex(a+x.a,b+x.b);}
12     Complex operator-(const Complex &x)const{return Complex(a-x.a,b-x.b);}
13     Complex operator*(const Complex &x)const{return Complex(a*x.a-b*x.b,a*x.b+b*x.a);}
14     Complex &operator*=(const Complex &x){return *this=*this*x;}
15 }A[maxn],B[maxn],C[maxn],D[maxn],E[maxn],F[maxn],I[maxn];//D是A每项取平方,E是b每项取平方,F是b每项取立方,I是伪单位元
16 void FFT(Complex*,int,int);
17 char S[maxn],T[maxn];
18 int n,m,N=1,ans=0;
19 int main(){
20     freopen("guess.in","r",stdin);
21     freopen("guess.out","w",stdout);
22     scanf("%s%s",S,T);
23     n=strlen(S);
24     m=strlen(T);
25     while(N<n+m)N<<=1;
26     for(int i=0;i<N;i++)I[i].a=1.0;
27     for(int i=0;i<n;i++){
28         A[i].a=S[i]-'a'+1;
29         D[i].a=A[i].a*A[i].a;
30     }
31     reverse(T,T+m);
32     for(int i=0;i<m;i++){
33         B[i].a=(T[i]=='?')?0:T[i]-'a'+1;
34         E[i].a=B[i].a*B[i].a;
35         F[i].a=B[i].a*E[i].a;
36     }
37     FFT(A,N,1);
38     FFT(B,N,1);
39     FFT(D,N,1);
40     FFT(E,N,1);
41     FFT(F,N,1);
42     FFT(I,N,1);//woc,这么多FFT慢不慢啊
43     for(int i=0;i<N;i++){
44         D[i]*=B[i];
45         A[i]*=E[i];
46         F[i]*=I[i];//printf("I[%d]=(%lf,%lf)\n",i,I[i].a,I[i].b);
47     }
48     FFT(A,N,-1);
49     FFT(D,N,-1);
50     FFT(F,N,-1);
51     for(int i=0;i<n;i++){
52         //printf("i=%d i+m-1=%d  A[i+m-1]=(%lf,%lf)  D[i+m-1]=(%lf,%lf)  F[i+m-1]=(%lf,%lf)\n",i,i+m-1,A[i+m-1].a,A[i+m-1].b,D[i+m-1].a,D[i+m-1].b,F[i+m-1].a,F[i+m-1].b);
53         C[i].a=D[i].a-A[i].a*2+F[i].a;    
54     }
55     for(int i=0;i+m<=n;i++)if(C[i+m-1].a<eps)ans++;
56     printf("%d\n",ans);
57     for(int i=0;i+m<=n;i++)if(C[i+m-1].a<eps)printf("%d\n",i);
58     return 0;
59 }
60 void FFT(Complex *A,int n,int tp){
61     for(int i=1,j=0,k;i<n-1;i++){
62         k=N;
63         do{
64             k>>=1;
65             j^=k;
66         }while(j<k);
67         if(i<j)swap(A[i],A[j]);
68     }
69     for(int k=2;k<=n;k<<=1){
70         Complex wn(cos(-tp*2*pi/k),sin(-tp*2*pi/k));
71         for(int i=0;i<n;i+=k){
72             Complex w(1.0,0.0);
73             for(int j=0;j<(k>>1);j++,w*=wn){
74                 Complex a=A[i+j],b=w*A[i+j+(k>>1)];
75                 A[i+j]=a+b;
76                 A[i+j+(k>>1)]=a-b;
77             }
78         }
79     }
80     if(tp<0)for(int i=0;i<n;i++)A[i].a/=n;
81 }
82 /*
83 把T反转,把S和T变成多项式a和b,令
84 c[i+m-1]=sum{(a[i+j]-b[m-j-1])^2*b[m-j-1]}
85 显然只有c是0的位置两串才会匹配,展开得
86 c[i+m-1]=sum{a[i+j]^2*b[m-j-1]-2*a[i+j]*b[m-j-1]^2+b[m-j-1]^3}
87 前两项是卷积形式,第三项乘上一个伪单位元也是卷积形式,FFT加速即可
88 */
View Code

ps:其实对$I$跑的那个DFT完全没必要,手动逐项赋成1就行了,然后发现$F$卷完了$I$根本没有什么变化,所以直接对$F$做一下DFT再IDFT回去即可,把$I$写进去只是为了理解式子方便......

UPD:脑残了……那个ps是错的,忽视掉吧……

posted @ 2017-01-26 18:58  AntiLeaf  阅读(288)  评论(0编辑  收藏  举报