第一道自己写的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位置匹配,否则不匹配。

 #include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxn=;
const double pi=acos(-1.0),eps=1e-;
struct Complex{
double a,b;
Complex(double a=0.0,double b=0.0):a(a),b(b){}
Complex operator+(const Complex &x)const{return Complex(a+x.a,b+x.b);}
Complex operator-(const Complex &x)const{return Complex(a-x.a,b-x.b);}
Complex operator*(const Complex &x)const{return Complex(a*x.a-b*x.b,a*x.b+b*x.a);}
Complex &operator*=(const Complex &x){return *this=*this*x;}
}A[maxn],B[maxn],C[maxn],D[maxn],E[maxn],F[maxn],I[maxn];//D是A每项取平方,E是b每项取平方,F是b每项取立方,I是伪单位元
void FFT(Complex*,int,int);
char S[maxn],T[maxn];
int n,m,N=,ans=;
int main(){
freopen("guess.in","r",stdin);
freopen("guess.out","w",stdout);
scanf("%s%s",S,T);
n=strlen(S);
m=strlen(T);
while(N<n+m)N<<=;
for(int i=;i<N;i++)I[i].a=1.0;
for(int i=;i<n;i++){
A[i].a=S[i]-'a'+;
D[i].a=A[i].a*A[i].a;
}
reverse(T,T+m);
for(int i=;i<m;i++){
B[i].a=(T[i]=='?')?:T[i]-'a'+;
E[i].a=B[i].a*B[i].a;
F[i].a=B[i].a*E[i].a;
}
FFT(A,N,);
FFT(B,N,);
FFT(D,N,);
FFT(E,N,);
FFT(F,N,);
FFT(I,N,);//woc,这么多FFT慢不慢啊
for(int i=;i<N;i++){
D[i]*=B[i];
A[i]*=E[i];
F[i]*=I[i];//printf("I[%d]=(%lf,%lf)\n",i,I[i].a,I[i].b);
}
FFT(A,N,-);
FFT(D,N,-);
FFT(F,N,-);
for(int i=;i<n;i++){
//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);
C[i].a=D[i].a-A[i].a*+F[i].a;
}
for(int i=;i+m<=n;i++)if(C[i+m-].a<eps)ans++;
printf("%d\n",ans);
for(int i=;i+m<=n;i++)if(C[i+m-].a<eps)printf("%d\n",i);
return ;
}
void FFT(Complex *A,int n,int tp){
for(int i=,j=,k;i<n-;i++){
k=N;
do{
k>>=;
j^=k;
}while(j<k);
if(i<j)swap(A[i],A[j]);
}
for(int k=;k<=n;k<<=){
Complex wn(cos(-tp**pi/k),sin(-tp**pi/k));
for(int i=;i<n;i+=k){
Complex w(1.0,0.0);
for(int j=;j<(k>>);j++,w*=wn){
Complex a=A[i+j],b=w*A[i+j+(k>>)];
A[i+j]=a+b;
A[i+j+(k>>)]=a-b;
}
}
}
if(tp<)for(int i=;i<n;i++)A[i].a/=n;
}
/*
把T反转,把S和T变成多项式a和b,令
c[i+m-1]=sum{(a[i+j]-b[m-j-1])^2*b[m-j-1]}
显然只有c是0的位置两串才会匹配,展开得
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}
前两项是卷积形式,第三项乘上一个伪单位元也是卷积形式,FFT加速即可
*/

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

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

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

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

    2216. 你猜是不是KMP ★★★☆   输入文件:guess.in   输出文件:guess.out   简单对比时间限制:1 s   内存限制:256 MB [题目描述] XX在玩两个串的游戏. ...

  2. FFT板子

    woc......FFT这玩意儿真坑...... 一上午除了打了几遍板子什么也没干......真是废了...... 你要加油啊...... #include<cstdio> #includ ...

  3. OI总结(垃圾排版就忽略了吧)

    学OI一年了,到现在联赛所需要的知识已经基本学完了.现在,有必要回过头来,总结总结自己一年来学到的知识以及得到的经验教训. 基础 语言基础 C++的语言基础啥的就略了吧. 算法复杂度分析 O:复杂度的 ...

  4. KMP算法求解

    // KMP.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include<iostream> using namespac ...

  5. 简单有效的kmp算法

    以前看过kmp算法,当时接触后总感觉好深奥啊,抱着数据结构的数啃了一中午,最终才大致看懂,后来提起kmp也只剩下“奥,它是做模式匹配的”这点干货.最近有空,翻出来算法导论看看,原来就是这么简单(先不说 ...

  6. KMP算法

    KMP算法是字符串模式匹配当中最经典的算法,原来大二学数据结构的有讲,但是当时只是记住了原理,但不知道代码实现,今天终于是完成了KMP的代码实现.原理KMP的原理其实很简单,给定一个字符串和一个模式串 ...

  7. 萌新笔记——用KMP算法与Trie字典树实现屏蔽敏感词(UTF-8编码)

    前几天写好了字典,又刚好重温了KMP算法,恰逢遇到朋友吐槽最近被和谐的词越来越多了,于是突发奇想,想要自己实现一下敏感词屏蔽. 基本敏感词的屏蔽说起来很简单,只要把字符串中的敏感词替换成"* ...

  8. [KMP]【学习笔记】

    Oulipo Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 36916   Accepted: 14904 Descript ...

  9. KMP算法实现

    链接:http://blog.csdn.net/joylnwang/article/details/6778316 KMP算法是一种很经典的字符串匹配算法,链接中的讲解已经是很明确得了,自己按照其讲解 ...

随机推荐

  1. 2016级算法第五次上机-A.Beihang Collegiate Pronunciation Contest 2017

    1065 Beihang Collegiate Pronunciation Contest 2017 思路 在字符串中不断做匹配 找到一个匹配就输出 时间复杂度\(O(n)\) ps.模式串是定长的, ...

  2. js控制输入框只能输入数字不能输入其他字符

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  3. Kettle 解决数据锁的问题(事务(进程 ID 51)与另一个进程被死锁在 锁 资源上)

    1.Kettle做了一个作业, 执行的时候问题发生在步骤2和步骤3之间,也就是步骤2还未完全执行完的时候,步骤3就要更新步骤2插入的数据,造成死锁.(我的理解是既然都分开作业了,那么每个作业都是一个单 ...

  4. mysql 2006 go away 错误

    https://blog.csdn.net/yypsober/article/details/71330673 原文地址.

  5. 停不下来队 Scrum Meeting 博客汇总

    停不下来队 Scrum Meeting 博客汇总 一.Alpha阶段 [Alpha]Scrum Meeting#1 [Alpha]Scrum Meeting#2 [Alpha]Scrum Meetin ...

  6. 基础篇:6.4)形位公差-基准 Datum

    本章目标:了解形位公差基准及运用. 1.定义: 基准 —  与被测要素有关且用来定义其几何位置关系的一个几何理想要素(如轴线.直线.平面等): —  可由零件上的一个或多个基准要素构成. 模拟基准要素 ...

  7. HTML学习-01

    1.标签描述了基本的链接地址/链接目标,该标签作为HTML文档中所有的链接标签的默认链接. 2.如果<head>里面设置了base,那么后面的img图片需要添加的相对路径. 3.不能使用工 ...

  8. oracle 行列转换函数之WM_CONCAT和LISTAGG的使用(一)

    一.wm_concat函数 wm_concat能够实现同样的功能,但是有时在11g中使用需要用to_char()进行转换,否则会出现不兼容现象(WMSYS.WM_CONCAT: 依赖WMSYS 用户, ...

  9. MySQL 的数据库、表基本操作

    1.链接数据库 mysql -u root -ppassword 2创建数据库 create database mr_book; 3选择数据库 use mr_book; 4 创建表 create ta ...

  10. 游戏反编译工具dnSpy

    dnSpy使用的工具下载地址为: https://github.com/cnxy/dnSpy/archive/v4.0.0.zip 或 dnSpy官方下载地址: https://github.com/ ...