【洛谷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即可 注意处理进位 ...
随机推荐
- vuex 精简demo解析
1.store/index.js 文件 import Vue from 'vue' import Vuex from 'vuex' //使用vuex Vue.use(Vuex); //一个store ...
- 人生第一次研读MFC截图工具的笔记心得
截图工具: 其中用到了动态链接库DLL技术(Dynamic Link Library)技术,键盘钩子技术,光标捕获技术,类橡皮类CRectTracker 头文件:后缀名为.cpp,主要是定义和声明之类 ...
- C# 类库项目 无法创建 “资源字典” 文件
1.接触WPF有两个月时间了,准备自己写一个样式库,在vs新建 类库项目后无法创建资源字典. 2.解决办法: 打开项目工程文件 ( project.csproj) 在 <Proper ...
- 剑指Offer-38.平衡二叉树(C++/Java)
题目: 输入一棵二叉树,判断该二叉树是否是平衡二叉树. 分析: 可以从根节点开始遍历每一个节点,求得节点左右子树的最大高度,判断是不是平衡二叉树.这样做的问题在于会重复遍历节点,造成不必要的浪费. 所 ...
- str函数之不同变量之间如何连接,外加浮点运算注意事项
最近看书了解到不同字符串之间可以用“+”号来连接,遂思考如何将不同变量连接起来,思考试验并上网查询后得出了结果,在此将所学分享给在阅读的各位. 数据类型的转换 常识可知,在python中,不同的数 ...
- IT兄弟连 HTML5教程 HTML5做到了与之前版本的兼容
为了保证HTML5能与之前的HTML版本达到最大的兼容,HTML5对一些元素标记的省略.boolean值的属性,以及引号的省略这几方面进行了兼顾,确保与之前版本的HTML达到兼容.在下面示例中,将本节 ...
- Web安全测试学习笔记-DVWA-SQL注入-1
SQL注入的定义网上很多,作为一个初学者,我对SQL注入的理解是这样的:网站应用一般都有后台数据库(不论是关系型还是非关系型),用户在网站上的绝大部分操作,最终都会跟数据库交互(也就是执行一串SQL语 ...
- RobotFramework - IF、FOR语句使用
一.IF 语句 1. IF语句简单实现 < Log 111111 2. IF语句赋值实现 ${rst} Set Variable If 1 < 2 2 1 3. IF...ELSE实现 $ ...
- ASP.NET Core SignalR:基础概述
一.简介 ASP.NET Core SignalR 是一个开源代码库,它简化了向应用添加实时 Web 功能的过程. 实时 Web 功能使服务器端代码能够即时将内容推送到客户端. SignalR 的适用 ...
- openstack 搭建
#所有节点修改ip,主机名和hosts解析 controller 10.0.0.11 controller compute1 10.0.0.31 compute1 #所有节点准备本地repo源 rm ...