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算法是一种很经典的字符串匹配算法,链接中的讲解已经是很明确得了,自己按照其讲解 ...
随机推荐
- luogu P1518 两只塔姆沃斯牛 The Tamworth Two
luogu P1518 两只塔姆沃斯牛 The Tamworth Two 题目描述 两只牛逃跑到了森林里.农夫John开始用他的专家技术追捕这两头牛.你的任务是模拟他们的行为(牛和John). 追击在 ...
- 最新的PHP trait使用方法详解
说通俗点,PHP中使用trait关键字是为了解决一个类既想集成基类的属性和方法,又想拥有别的基类的方法,而trait一般情况下是和use搭配使用的. 具体案例,查看php中文网这篇文章.http:// ...
- 2016级算法第一次练习赛-C.斐波那契进阶
870 斐波那契进阶 题目链接:https://buaacoding.cn/problem/870/index 思路 通过读题就可以发现这不是一般的求斐波那契数列,所以用数组存下所有的答案是不现实的. ...
- Hibernate 使用log4j日志记录
日志记录使程序员能够将日志详细信息永久写入文件.这是我们以后在开发当中非常重要的一步. Hibernate使用log4j日志记录,我们需要以下几个步骤: 1.导入jar包: (1)这是hibernat ...
- 适配器模式-如何把usb插到插座上
前言 下面所写的内容不是实际的业务场景, 也可能不符合正常的生活习惯, 或者不满足一些人的口味 所写的内容包括之前的帖子,只是为了方便大家更好的记住这个设计模式,实际生活中要灵活应用 设计模式重思想, ...
- paraview鼠标选择网格
虽然可以根据ID选择网格,但是有时候需要选择可见面,直接鼠标比较方便,可以直接按一下键盘"S",这时候鼠标变成十字型,然后鼠标左键选择区域.
- CDQZ Day3
模拟题 day3出题人: liu_runda题目名称 摆渡 摆车 背包源程序文件名 boat.cpp ju.cpp pack.cpp输入文件名 boat.in ju.in pack.in输出文件名 b ...
- 【Python】批量检测百度权重
挖洞过程中收集了站点后,我一般习惯查看站点的百度权重值,为了方便,写了一个简单的脚本, 至于结果如何显示,看个人需求吧,我这里只是简单的列一下,脚本如下: #coding:utf-8 import r ...
- php+windows环境安装
1.下载并安装phpstorm 2.查找激活码激活phpstorm 3.由于php官方没有提供exe版本,安装phpstudy,获得windows下exe编译版本的php 4.安装VisualSVN ...
- Jmeter性能测试之添加思考时间
利用定时器添加用户思考时间 JMeter如何插入思考时间,在一个真实的性能测试场景中,是需要加入思考时间,来模拟真实用户行为.本文就来介绍,如何在三个请求之间添加思考时间. 1. 在Test Plan ...