【洛谷4173】残缺的字符串(重拾FFT)
大致题意: 有一个长度为\(n\)的字符串\(A\)和一个长度为\(m\)的字符串\(B\),其中存在一些字符'*'可以与任意字符匹配。求\(B\)中所有满足条件的位置,使得从这一位置开始的连续\(n\)个字符组成的字符串能与\(A\)完全匹配。
前言
之前一直对\(FFT\)这个算法不太理解,最近感觉数学水平有了点小提升,于是去重新看了遍\(FFT\)的原理,突然发现能大致理解了......
这道题原题是\(A\)长度为\(m\),\(B\)长度为\(n\),出于个人习惯,把\(n\)和\(m\)调换了一下,变成\(A\)长度为\(n\),\(B\)长度为\(m\)。
同样出于个人习惯,我还把\(n\)和\(m\)各自减了\(1\),则\(A\)中下标为\(0\sim n\),\(B\)中下标为\(0\sim m\),由于题目要求下标从\(1\)开始,所以最后又将答案加了\(1\)。
希望能理解。
\(FFT\)与字符串匹配
\(FFT\)与字符串匹配,看似没有任何联系,实际上却大有用处。
例如这题,首先我们可以把字符转化成数字,'a'~'z'各自对应\(1\sim 26\),而'*'就用\(0\)来表示。
先不考虑'*',此时如果要比较两个字符\(x,y\)是否匹配,显然我们可以发现若\(x=y\),即\(x-y=0\),两个字符匹配,而若\(x-y≠0\),则两个字符不匹配。
也就是说,我们这里把\(x-y\)看作是匹配值,匹配值为\(0\)表示匹配,不为\(0\)表示不匹配。
但是,如果要比较两个字符串是否匹配,却不能单纯把每一位上\(x-y\)的值加一起来判断是否\(0\),因为\(x-y\)可正可负,加一起可能会相互抵消成\(0\)。
怎么办?
把负号去掉不就行了吗?首先便会想到把匹配值改为\(|x-y|\),但这似乎不太好操作。
但是,如果改为\((x-y)^2\),似乎就很可行的样子。
总结一下,要比较两个长度相等的字符串是否完全匹配,我们可以对于每一位求出\((x-y)^2\),然后求和,如果最后的和为\(0\),说明完全匹配,否则说明不匹配。
那出现了'*'呢?
由于'*'可以与任意字符匹配,且上面我们决定了用\(0\)来表示''*',而完全匹配的定义又是匹配值为\(0\)。
所以,我们可以把匹配值改为\(xy(x-y)^2\),如果这个式子求和为\(0\),就说明完全匹配。
推式子
假设我们现在要判断从\(B\)中第\(i\)位开始的连续\(n\)个字符是否能与\(A\)完全匹配。
则就是要求:
\]
拆开得到:
\]
考虑设\(T_{k}=A_{n-k}\),即\(A\)串的倒串,则原式等同于:
\]
然后我们就能发现一件很神奇的是:每对相乘的项下标之和都是\(n+i\),在\(i\)一定时是个定值!
所以,我们可以把上面的式子改成卷积形式,即:
\]
这么一来,我们只要对\(T,T^2,T^3,B,B^2,B^3\)六个多项式分别做\(DFT\),进行上述运算后,再做\(IDFT\),即可得到一个新的多项式\(P\)。
而从\(B\)中第\(i\)位开始的连续\(n\)个字符能与\(A\)完全匹配,当且仅当\(P\)中第\(n+i\)项的系数为\(0\)。
这样这题就做完了。
代码
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 300000
#define LL long long
#define DB double
using namespace std;
int n,m,a[4*N+5],b[4*N+5];char s[N+5];
template<int SZ> class Poly
{
private:
int P,L,R[4*N+5];DB Pi;
struct node
{
DB x,y;I node(Con DB& a=0,Con DB& b=0):x(a),y(b){}
I node operator + (Con node& o) Con {return node(x+o.x,y+o.y);}
I node operator - (Con node& o) Con {return node(x-o.x,y-o.y);}
I node operator * (Con node& o) Con {return node(x*o.x-y*o.y,x*o.y+y*o.x);}
}A[4*N+5],A2[4*N+5],A3[4*N+5],B[4*N+5],B2[4*N+5],B3[4*N+5];
I void T(node *s,CI op)
{
RI i,j,k;node U,S,x,y;for(i=0;i^P;++i) i<R[i]&&(U=s[i],s[i]=s[R[i]],s[R[i]]=U,0);
for(i=1;i^P;i<<=1) for(U=node(cos(Pi/i),op*sin(Pi/i)),j=0;j^P;j+=i<<1)
for(S=node(1,0),k=0;k^i;++k,S=S*U) s[j+k]=(x=s[j+k])+(y=S*s[i+j+k]),s[i+j+k]=x-y;
}
public:
I Poly() {Pi=acos(-1);}
Tp I void FFT(CI n,CI m,Ty *a,Ty *b)
{
RI i;P=1,L=0;W(P<=n+m) P<<=1,++L;for(i=0;i^P;++i) R[i]=(R[i>>1]>>1)|((i&1)<<L-1);
for(i=0;i<=n;++i) A[i]=a[i],A2[i]=A[i]*A[i],A3[i]=A[i]*A2[i];//记录多项式
for(i=0;i<=m;++i) B[i]=b[i],B2[i]=B[i]*B[i],B3[i]=B[i]*B2[i];//记录多项式
T(A,1),T(A2,1),T(A3,1),T(B,1),T(B2,1),T(B3,1);//DFT
for(i=0;i^P;++i) A[i]=A3[i]*B[i]-node(2)*A2[i]*B2[i]+A[i]*B3[i];//运算
for(T(A,-1),i=0;i<=m;++i) a[i]=A[i].x/P+0.5;//IDFT
}
};Poly<N> P;
int main()
RI i,t=0;scanf("%d%d",&n,&m),--n,--m;
for(scanf("%s",s),i=0;i<=n;++i) a[n-i]=s[i]=='*'?0:(s[i]&31);//注意A串取倒串
for(scanf("%s",s),i=0;i<=m;++i) b[i]=s[i]=='*'?0:(s[i]&31);
for(P.FFT(n,m,a,b),i=0;i<=m-n;++i) t+=!a[n+i];printf("%d\n",t);//统计个数
for(i=0;i<=m-n;++i) !a[n+i]&&printf("%d ",i+1);return 0;//输出合法位置
}
【洛谷4173】残缺的字符串(重拾FFT)的更多相关文章
- 洛谷 P4173 残缺的字符串 (FFT)
题目链接:P4173 残缺的字符串 题意 给定长度为 \(m\) 的模式串和长度为 \(n\) 的目标串,两个串都带有通配符,求所有匹配的位置. 思路 FFT 带有通配符的字符串匹配问题. 设模式串为 ...
- 洛谷P4173 残缺的字符串(FFT)
传送门 话说为什么字符串会和卷积扯上关系呢……到底得脑洞大到什么程度才能想到这种东西啊……大佬太珂怕了…… 因为通配符的关系,自动机已经废了 那么换种方式考虑,如果两个字符串每一位对应的编码都相等,那 ...
- 洛谷P4173 残缺的字符串
题目大意: 两个带通配符的字符串\(a,b\),求\(a\)在\(b\)中出现的位置 字符串长度\(\le 300000\) 考虑魔改一发\(kmp\),发现魔改不出来 于是考虑上网搜题解 然后考虑\ ...
- 洛谷 P4173 残缺的字符串
(不知道xjb KMP可不可以做的说) (假设下标都以0开头) 对于有一定偏移量的序列的 对应位置 匹配或者数值计算的题,这里是有一种套路的,就是把其中一个序列翻转过来,然后卷积一下,所得到的新序列C ...
- BZOJ1856或洛谷1641 [SCOI2010]生成字符串
BZOJ原题链接 洛谷原题链接 可以将\(1\)和\(0\)的个数和看成是\(x\)轴坐标,个数差看成\(y\)轴坐标. 向右上角走,即\(x\)轴坐标\(+1\),\(y\)轴坐标\(+1\),表示 ...
- [洛谷P4768] [NOI2018]归程 (kruskal重构树模板讲解)
洛谷题目链接:[NOI2018]归程 因为题面复制过来有点炸格式,所以要看题目就点一下链接吧\(qwq\) 题意: 在一张无向图上,每一条边都有一个长度和海拔高度,小\(Y\)的家在\(1\)节点,并 ...
- 卡特兰数 洛谷P1641 [SCOI2010]生成字符串
卡特兰数 参考博客 介绍 卡特兰数为组合数学中的一种特殊数列,用于解决一类特殊问题 设\(f(n)\)为卡特兰数的第n项 其通项公式为 \[f(n)=\frac{2n\choose n}{n+1} \ ...
- 洛谷 P1641 [SCOI2010]生成字符串
洛谷 这题一看就是卡塔兰数. 因为\(cnt[1] \leq cnt[0]\),很显然的卡塔兰嘛! 平时我们推导卡塔兰是用一个边长为n的正方形推的, 相当于从(0,0)点走到(n,n)点,向上走的步数 ...
- 洛谷P1852 奇怪的字符串
题目描述 输入两个01串,输出它们的最长公共子序列的长度 输入输出格式 输入格式: 一行,两个01串 输出格式: 最长公共子序列的长度 输入输出样例 输入样例#1: 复制 01010101010 00 ...
- 洛谷.1919.[模板]A*B Problem升级版(FFT)
题目链接:洛谷.BZOJ2179 //将乘数拆成 a0*10^n + a1*10^(n-1) + ... + a_n-1的形式 //可以发现多项式乘法就模拟了竖式乘法 所以用FFT即可 注意处理进位 ...
随机推荐
- Node.js C++ 插件学习指南
Node.js插件(addons) Node.js 插件是用 C++ 编写的动态链接共享对象,可以使用 require() 函数加载到 Node.js 中,且像普通的 Node.js 模块一样被使用. ...
- Data Guard Physical Standby - RAC Primary to RAC Standby 使用第二个网络 (Doc ID 1349977.1)
Data Guard Physical Standby - RAC Primary to RAC Standby using a second network (Doc ID 1349977.1) A ...
- 表单生成器(Form Builder)之伪造表单数据番外篇——指定范围随机时间
为了伪造一些尽量真实的假数据,也真是够费劲的.上一篇笔记记录了一下获取一个随机车辆牌照,这篇笔记记录一下怎么获取一个随机时间.这篇就不说那么多废话了,直接上代码 // 获取指定范围的随机数 var g ...
- [C]#include和链接
概述 对于刚接触C语言的同学来说,通常对“在文件中用#include预处理操作符引入文件”和“编译时链接多个文件”这两个操作会有所混淆,这个文章主要为了解析一下它们的区别. #include预处理操作 ...
- linux-在指定路径下查询文件夹是否存在
我们常常在Linux下去查找文件 find / -name 'test.py' # 在根目录下查找名为test.py的文件 但是如果用查找文件的方式去查找文件夹的话,是查不到的 find / -max ...
- 最强Linux shell工具Oh My Zsh 指南
引言 笔者已经使用zsh一年多了,发现这个东东的功能太强大了.接下来,给大家推荐一下. 以下是oh-my-zsh部分功能 命令验证 在所有正在运行的shell中共享命令历史记录 拼写纠正 主题提示(A ...
- Unity ugui屏幕适配与世界坐标到ugui屏幕坐标的转换
我们知道,如今的移动端设备分辨率五花八门,而开发过程中往往只取一种分辨率作为设计参考,例如采用1920*1080分辨率作为参考分辨率. 选定了一种参考分辨率后,美术设计人员就会固定以这样的分辨率来设计 ...
- 【docker构建】基于docker搭建redis数据库缓存服务
搭建redis服务 首先拉取镜像==>运行镜像为容器 # 先搜索下redis镜像 [root@VM_0_10_centos wordpress]# docker search redis # 拉 ...
- Web安全测试学习笔记-DVWA-存储型XSS
XSS(Cross-Site Scripting)大致分为反射型和存储型两种,之前对XSS的认知仅停留在如果网站输入框没有屏蔽类似<script>alert('ok')</scrip ...
- 高并发 Nginx+Lua OpenResty系列(11)——流量复制/AB测试/协程
流量复制 在实际开发中经常涉及到项目的升级,而该升级不能简单的上线就完事了,需要验证该升级是否兼容老的上线,因此可能需要并行运行两个项目一段时间进行数据比对和校验,待没问题后再进行上线.这其实就需要进 ...