点此看题面

大致题意: 有一个长度为\(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\)完全匹配。

则就是要求:

\[\sum_{k=0}^nA_kB_{i+k}(A_k-B_{i+k})^2
\]

拆开得到:

\[\sum_{k=0}^n(A_k^3B_{i+k}-2A_k^2B_{i+k}^2+A_kB_{i+k}^3)
\]

考虑设\(T_{k}=A_{n-k}\),即\(A\)串的倒串,则原式等同于:

\[\sum_{k=0}^n(T_{n-k}^3B_{i+k}-2T_{n-k}^2B_{i+k}^2+T_{n-k}B_{i+k}^3)
\]

然后我们就能发现一件很神奇的是:每对相乘的项下标之和都是\(n+i\),在\(i\)一定时是个定值!

所以,我们可以把上面的式子改成卷积形式,即:

\[\sum_{x+y=n+i}(T_{x}^3B_{y}-2T_{x}^2B_{y}^2+T_{x}B_{y}^3)
\]

这么一来,我们只要对\(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)的更多相关文章

  1. 洛谷 P4173 残缺的字符串 (FFT)

    题目链接:P4173 残缺的字符串 题意 给定长度为 \(m\) 的模式串和长度为 \(n\) 的目标串,两个串都带有通配符,求所有匹配的位置. 思路 FFT 带有通配符的字符串匹配问题. 设模式串为 ...

  2. 洛谷P4173 残缺的字符串(FFT)

    传送门 话说为什么字符串会和卷积扯上关系呢……到底得脑洞大到什么程度才能想到这种东西啊……大佬太珂怕了…… 因为通配符的关系,自动机已经废了 那么换种方式考虑,如果两个字符串每一位对应的编码都相等,那 ...

  3. 洛谷P4173 残缺的字符串

    题目大意: 两个带通配符的字符串\(a,b\),求\(a\)在\(b\)中出现的位置 字符串长度\(\le 300000\) 考虑魔改一发\(kmp\),发现魔改不出来 于是考虑上网搜题解 然后考虑\ ...

  4. 洛谷 P4173 残缺的字符串

    (不知道xjb KMP可不可以做的说) (假设下标都以0开头) 对于有一定偏移量的序列的 对应位置 匹配或者数值计算的题,这里是有一种套路的,就是把其中一个序列翻转过来,然后卷积一下,所得到的新序列C ...

  5. BZOJ1856或洛谷1641 [SCOI2010]生成字符串

    BZOJ原题链接 洛谷原题链接 可以将\(1\)和\(0\)的个数和看成是\(x\)轴坐标,个数差看成\(y\)轴坐标. 向右上角走,即\(x\)轴坐标\(+1\),\(y\)轴坐标\(+1\),表示 ...

  6. [洛谷P4768] [NOI2018]归程 (kruskal重构树模板讲解)

    洛谷题目链接:[NOI2018]归程 因为题面复制过来有点炸格式,所以要看题目就点一下链接吧\(qwq\) 题意: 在一张无向图上,每一条边都有一个长度和海拔高度,小\(Y\)的家在\(1\)节点,并 ...

  7. 卡特兰数 洛谷P1641 [SCOI2010]生成字符串

    卡特兰数 参考博客 介绍 卡特兰数为组合数学中的一种特殊数列,用于解决一类特殊问题 设\(f(n)\)为卡特兰数的第n项 其通项公式为 \[f(n)=\frac{2n\choose n}{n+1} \ ...

  8. 洛谷 P1641 [SCOI2010]生成字符串

    洛谷 这题一看就是卡塔兰数. 因为\(cnt[1] \leq cnt[0]\),很显然的卡塔兰嘛! 平时我们推导卡塔兰是用一个边长为n的正方形推的, 相当于从(0,0)点走到(n,n)点,向上走的步数 ...

  9. 洛谷P1852 奇怪的字符串

    题目描述 输入两个01串,输出它们的最长公共子序列的长度 输入输出格式 输入格式: 一行,两个01串 输出格式: 最长公共子序列的长度 输入输出样例 输入样例#1: 复制 01010101010 00 ...

  10. 洛谷.1919.[模板]A*B Problem升级版(FFT)

    题目链接:洛谷.BZOJ2179 //将乘数拆成 a0*10^n + a1*10^(n-1) + ... + a_n-1的形式 //可以发现多项式乘法就模拟了竖式乘法 所以用FFT即可 注意处理进位 ...

随机推荐

  1. 【计算机网络】ISO/OSI 网络体系结构

    ISO/OSI 网络体系结构 计算机网络是相当复杂的系统,相互通信的两个计算机系统必须高度协调才能正常工作.为了设计这样复杂的计算机网络,人们提出了将网络分层的方法.分层可将庞大而复杂的问题转换为若干 ...

  2. NFS挂载遇到的问题

    问题描述:生产环境中需要经常运用NFS挂载,就在测试环境中测试一下,将服务器中192.168.1.4 /u01/app/oracle/product/11.2.0/dbhome_1/dbs  挂载到1 ...

  3. Reinforcement Learning by Sutton 第三章习题答案

    好不容易写完了 想看全部的欢迎点击下面的github https://github.com/LyWangPX/Solutions-of-Reinforcement-Learning-An-Introd ...

  4. BITCTF-MISC

    MISC 以此笔记来记录本菜鸡做misc的历程 签到85 首先看题 提示base85 打开kali,使用python的base64库来解码(内有base85解码) (其实只要输python3即可 我还 ...

  5. 斐波那契数列(Java)

    一.什么是斐波那契数列 斐波那契数列(Fibonacci sequence),又称黄金分割数列.因数学家列昂纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖为例子而引入,故又称为& ...

  6. spanish-1.1

    1.1. ¿Cómo te llamas?Ana : ¿Cómo te llamas?Paco: Buenos dias. Yo soy Paco. Y tú, ¿cómo te llemas?Ana ...

  7. SQl Server 中的decimal( m , n )的意思

    create table sc( cno ), sno ), grade ,), primary key(cno,sno), foreign key(cno) references cou(cno), ...

  8. 2019年跨越速递Java工程师笔试题

    1.下面哪个选项可以用于JSP页面之间传递对象(A C) A application B page C session D error  E response 评语:这道题考察的是对JSP内置对象的了 ...

  9. 2019阿里天猫团队Java高级工程师面试题之第三面

    2019阿里天猫团队Java高级工程师面试题之第一面 2019阿里天猫团队Java高级工程师面试题之第二面 1.说说MySQL的锁并发?加锁的机制是什么? https://www.cnblogs.co ...

  10. C# MD5 32大写位加密 UTF-8编码

    string UserMd5(string str) { string cl = str; string pwd = ""; MD5 md5 = MD5.Create();//实例 ...