点此看题面

大致题意: 有一个长度为\(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. arcgis api for javascript 学习(一) 调用在线发布的动态地图

    1.图中显示为arcgis软件中显示的地图文件 2.调用动态地图主要的是知道动态地图的URL地址 3.通过IDE(webstorm)调用动态地图,如图 4.话不多说,直接上代码 <!DOCTYP ...

  2. 一条简单的更新语句,MySQL是如何加锁的?

    看如下一条sql语句: # table T (id )) delete : MySQL在执行的过程中,是如何加锁呢? 在看下面这条语句: : 那这条语句呢?其实这其中包含太多知识点了.要回答这两个问题 ...

  3. [PHP] swoole直接使用二进制包

    swoole提供一个编译好的二进制包,这个包连php都包含进去了,下载解压后就可以直接运行,都不用安装php 在这个地方直接下载二进制包 https://www.swoole.com/page/dow ...

  4. kubernetes搭建(可访问外网环境部署)

    版权声明:本文为博主原创文章,支持原创,转载请附上原文出处链接和本声明. 本文链接地址:https://www.cnblogs.com/wannengachao/p/11947621.html 一.前 ...

  5. Unity3D VidoePlayer 加载StreamingAssets下视频

    using System.Collections;using System.Collections.Generic;using UnityEngine;using UnityEngine.UI;usi ...

  6. layui多个时间选择器出现闪退问题

    1.出现问题的代码 laydate.render({ elem: '#startDate' // }); laydate.render({ elem: '#endDate' // }); laydat ...

  7. mongodb基本安装

    这次搞搞NOSQL, 但最简单的MONGODB安装,还是要作点配置的. 一,安装网址: https://www.mongodb.com/download-center/community?jmp=na ...

  8. opencv加载图片imread失败的原因

    用简单的imshow函数加载图片,报加载失败的异常,显示没有将图片加载到内存中.原因是在配置环境是同时将*lib与*d.lib都入了附加依赖项,而项目的生成方式选择的是debug,*lib在*d.li ...

  9. 递归找到多级文件夹中所有pdf文件的py程序

    因个人需要,写了一个可以递归找到多级文件夹中所有pdf的小程序,发布出来供有需要的人参考或使用. import os import re import shutil from os.path impo ...

  10. win10禁止自动更新的终极方法(亲测有效)

    想必用过win10的朋友对其自动更新一定不会陌生,并且深恶痛绝,    有时正专注做一件事,突然就开始自动更新,被杀个措手不及,而且更新时间真的太久了,尤其最近更新频繁,真是伤脑筋,    期间也尝试 ...