第一道自己写的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. nginx高性能WEB服务器系列之三版本升级

    nginx系列友情链接:nginx高性能WEB服务器系列之一简介及安装https://www.cnblogs.com/maxtgood/p/9597596.htmlnginx高性能WEB服务器系列之二 ...

  2. iOS 循环轮播框架思路

    使用3个imageview实现无线轮播的大致原理 将3个imageview添加到scrollview上面,scrollview的contensize是3个imageview的宽度,设置scrollvi ...

  3. 2016级算法第一次练习赛-B.朴素的中位数

    朴素的中位数 题目链接:https://buaacoding.cn/problem/846/index 分析 题意很简单,就是给定了两个从小到大排好序的数组,找出这两个数组合起来的数据中的中位数. 方 ...

  4. 使用IntelliJ IDEA 前最好修改的配置

    目录 1.下载 2.破解 3.修改配置 下载 详见我的另外一篇博客:<软件开发资源下载>中的[IDE]->[IntelliJ IDEA] 破解 详见我的另外一篇博客:<最新版I ...

  5. bzoj2959: 长跑(LCT+并查集)

    题解 动态树Link-cut tree(LCT)总结 LCT常数大得真实 没有环,就是\(lct\)裸题吧 有环,我们就可以绕环转一圈,缩点 怎么搞? 当形成环时,把所有点的值全部加到一个点上,用并查 ...

  6. Python爬虫常用之登录(三) 使用http请求登录

    前面说了使用浏览器登录较为简单,不需要过多分析,而使用请求登录恰恰就是以分析为主. 开发一个请求登录程序的流程: 分析请求->模拟请求->测试登录->调整参数->测试登录-&g ...

  7. Shiro入门资源整理

    学习一个框架,查阅权威有效的资料能够事半功倍,本文收集笔者学习此框架中帮助很大的文档,希望对大家有所帮助. 对于文档类的,强烈建议看官方文档,而不是百度出来的经过网友加工或者搬运过来的资料!! shi ...

  8. 使用Maven运行测试提示Module sdk 1.5的解决方法

    解决方法: 1. 配置Project Structure 2. 在MAVEN_HOME/conf/setting.xml中添加profile 3. 在Maven项目的pom.xml文件里添加标签 三种 ...

  9. element-ui tree树形组件自定义实现可展开选择表格

    最近做项目遇到一个需求,表格里可以展开,可以选择,大概效果如下图: 一开始是在table组件里找方法,使用了表格的合并方法,效果是实现了,但是表格的多选每次触发时,都会执行好几次,而且没法实现一部分的 ...

  10. ftp发送文件包括中文名

    public void sendwordToftp() {        try {            Json json = new Json();            String file ...