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 */

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

原文地址:http://www.cnblogs.com/hzoier/p/6351822.html

时间: 2024-10-25 01:03:19

COGS2216 你猜是不是KMP的相关文章

快速傅里叶变换(FFT):COGS 2216. 你猜是不是KMP

2216. 你猜是不是KMP ★★★☆   输入文件:guess.in   输出文件:guess.out   简单对比时间限制:1 s   内存限制:256 MB [题目描述] XX在玩两个串的游戏.首先,他拿出了两个字符串 S 和 T,XX想知道 T在 S 中出现了几次,分别在哪些位置出现.注意 T 中可能有“?”字符,这个字符可以匹配任何字符. [输入格式] 两行两个字符串,分别代表 S 和 T [输出格式] 第一行一个正整数 k,表示 T 在 S 中出现了几次. 接下来 k 行正整数, 分

Educational Codeforces Round 21 G. Anthem of Berland(dp+kmp)

题目链接:Educational Codeforces Round 21 G. Anthem of Berland 题意: 给你两个字符串,第一个字符串包含问号,问号可以变成任意字符串. 问你第一个字符串最多包含多少个第二个字符串. 题解: 考虑dp[i][j],表示当前考虑到第一个串的第i位,已经匹配到第二个字符串的第j位. 这样的话复杂度为26*n*m*O(fail). fail可以用kmp进行预处理,将26个字母全部处理出来,这样复杂度就变成了26*n*m. 状态转移看代码(就是一个kmp

hiho 1015 KMP算法 &amp;&amp; CF 625 B. War of the Corporations

#1015 : KMP算法 时间限制:1000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi和小Ho是一对好朋友,出生在信息化社会的他们对编程产生了莫大的兴趣,他们约定好互相帮助,在编程的学习道路上一同前进. 这一天,他们遇到了一只河蟹,于是河蟹就向小Hi和小Ho提出了那个经典的问题:“小Hi和小Ho,你们能不能够判断一段文字(原串)里面是不是存在那么一些……特殊……的文字(模式串)?” 小Hi和小Ho仔细思考了一下,觉得只能想到很简单的做法,但是又觉得既然河蟹先生这么说了,就

hdu2328 Corporate Identity 扩展KMP

Beside other services, ACM helps companies to clearly state their "corporate identity", which includes company logo but also other signs, like trademarks. One of such companies is Internet Building Masters (IBM), which has recently asked ACM for

KMP算法详解

这几天学习kmp算法,解决字符串的匹配问题,开始的时候都是用到BF算法,(BF(Brute Force)算法是普通的模式匹配算法,BF算法的思想就是将目标串S的第一个字符与模式串T的第一个字符进行匹配,若相等,则继续比较S的第二个字符和 T的第二个字符;若不相等,则比较S的第二个字符和T的第一个字符,依次比较下去,直到得出最后的匹配结果.BF算法是一种蛮力算法.)虽然也能解决一些问题,但是这是常规思路,在内存大,数据量小,时间长的情况下,还能解决一些问题,但是如果遇到一些限制时间和内存的字符串问

2016——3——16 kmp习题

1.传送门:http://begin.lydsy.com/JudgeOnline/problem.php?id=2725 题目大意:找一个串在另一个串中出现的次数 题解:kmp(纯裸题) 2.传送门:http://begin.lydsy.com/JudgeOnline/problem.php?id=2732 题目大意:给你一个字符串,让你求出最大重复周期(最大周期不和本身重合) 题解:kmp或者扩展kmp(但我并未用这种方法),我用的是kmp,但是一直WA,原来是求next数组把while写成了

KMP算法

1 /* next数组是KMP算法的关键,next数组的作用是:当模式串T和主串S失配 2 * ,next数组对应的元素指导应该用T串中的哪一个元素进行下一轮的匹配 3 * next数组和T串相关,和S串无关.KMP的关键是next数组的求法. 4 * 5 * ——————————————————————————————————————————————————————————————————— 6 * | T | 9 | a | b | a | b | a | a | a | b | a | 7

KMP

http://www.matrix67.com/blog/archives/115 http://www.cnblogs.com/c-cloud/p/3224788.html 1 #include<stdio.h> 2 #include<algorithm> 3 #include<iostream> 4 #include<string.h> 5 #include<stdlib.h> 6 #include<math.h> 7 #incl

朴素和KMP模式匹配算法(Java)

朴素模式匹配算法 public class Test { //朴素模式匹配算法 public int Index(String s,String t,int pos){ int i = pos;//主串中第几个位置开始比较 int j = 0;//模式串中的第一个位置 while(i<s.length()&&j<t.length()){ if(s.charAt(i)==t.charAt(j)){ i++; j++; }else { i = i-j+1;//主串的下一个位置 j