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位置匹配,否则不匹配。
#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的更多相关文章
- 快速傅里叶变换(FFT):COGS 2216. 你猜是不是KMP
2216. 你猜是不是KMP ★★★☆ 输入文件:guess.in 输出文件:guess.out 简单对比时间限制:1 s 内存限制:256 MB [题目描述] XX在玩两个串的游戏. ...
- FFT板子
woc......FFT这玩意儿真坑...... 一上午除了打了几遍板子什么也没干......真是废了...... 你要加油啊...... #include<cstdio> #includ ...
- OI总结(垃圾排版就忽略了吧)
学OI一年了,到现在联赛所需要的知识已经基本学完了.现在,有必要回过头来,总结总结自己一年来学到的知识以及得到的经验教训. 基础 语言基础 C++的语言基础啥的就略了吧. 算法复杂度分析 O:复杂度的 ...
- KMP算法求解
// KMP.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include<iostream> using namespac ...
- 简单有效的kmp算法
以前看过kmp算法,当时接触后总感觉好深奥啊,抱着数据结构的数啃了一中午,最终才大致看懂,后来提起kmp也只剩下“奥,它是做模式匹配的”这点干货.最近有空,翻出来算法导论看看,原来就是这么简单(先不说 ...
- KMP算法
KMP算法是字符串模式匹配当中最经典的算法,原来大二学数据结构的有讲,但是当时只是记住了原理,但不知道代码实现,今天终于是完成了KMP的代码实现.原理KMP的原理其实很简单,给定一个字符串和一个模式串 ...
- 萌新笔记——用KMP算法与Trie字典树实现屏蔽敏感词(UTF-8编码)
前几天写好了字典,又刚好重温了KMP算法,恰逢遇到朋友吐槽最近被和谐的词越来越多了,于是突发奇想,想要自己实现一下敏感词屏蔽. 基本敏感词的屏蔽说起来很简单,只要把字符串中的敏感词替换成"* ...
- [KMP]【学习笔记】
Oulipo Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 36916 Accepted: 14904 Descript ...
- KMP算法实现
链接:http://blog.csdn.net/joylnwang/article/details/6778316 KMP算法是一种很经典的字符串匹配算法,链接中的讲解已经是很明确得了,自己按照其讲解 ...
随机推荐
- c++11 perfect forwarding
完美转发是c++11 引入右值引用之后,在template 中的延伸. 顾名思义,完美转发是将参数不改变属性的条件下,转发给下一个函数. 因为普通函数的参数一旦具名,始终都是lvalue. 如果把rv ...
- 对cookie,session,token,jwt的理解
对这几个东西有点凌乱了,今天有时间整理下 cookie Cookie的诞生 由于HTTP协议是无状态的,而服务器端的业务必须是要有状态的.Cookie诞生的最初目的是为了存储web中的状态信息,以方便 ...
- 如何判断一个对象实例是不是某个类型,如Cat类型
<script> function cat(){} var b = new cat(); if(b instanceof cat){ console.log("a是cat&quo ...
- java面向对象概念2
一.理解“万事万物皆对象”. 1.在java语言范畴中,我们都将功能.结构等封装到类中,通过类的实例化,来调用具体的功能结构. 2.涉及到java语言与前端html.后端的数据库交互时,前后端的结构在 ...
- WeakHashMap源码分析
WeakHashMap是一种弱引用map,内部的key会存储为弱引用, 当jvm gc的时候,如果这些key没有强引用存在的话,会被gc回收掉, 下一次当我们操作map的时候会把对应的Entry整个删 ...
- SpringBoot入门(IDEA篇)(三)
一.什么是JPA JPA(Java Persistence API)定义了一系列对象持久化的标准,目前实现这一规范的产品有Hibernate.TopLink等. 二.Mysql数据库示例 1.在app ...
- document.referrer和history.go(-1)退回上一页区别
javascript:location=document.referrer;和javascript:history.go(-1);区别: 返回上一页,在PC端我们可以使用:history.go(-1) ...
- pip和conda安装源更改
pip和conda安装源更改 python模块安装,使用国内源可以提高下载速度. pip源更改: pip源有好几个,我一直用的清华的pip源,它5分钟同步一次. 临时使用: pip 后加参数 -i h ...
- SpringBoot集成WebSocket【基于纯H5】进行点对点[一对一]和广播[一对多]实时推送
代码全部复制,仅供自己学习用 1.环境搭建 因为在上一篇基于STOMP协议实现的WebSocket里已经有大概介绍过Web的基本情况了,所以在这篇就不多说了,我们直接进入正题吧,在SpringBoot ...
- 构造函数与普通函数关于“new”操作符
javascript中构造函数与普通函数的区别还有关于“new”操作符的一些原理 有一种创建对象的方法叫做工厂模式,例如: 1 function person(name,age){ 2 var o ...