【BZOJ4503】两个串(FFT)
【BZOJ4503】两个串(FFT)
题面
给定串\(S\),以及带通配符的串\(T\),询问\(T\)在\(S\)中出现了几次。并且输出对应的位置。
\(|S|,|T|<=10^5\),字符集大小为\(26\)
题解
先来考虑没有通配符怎么匹配。别跟我说KMP!!
根据前面几个题目的套路,我们可以把每个字符分开来考虑,然后将\(T\)串反转,将有这个字符的位置变成\(1\),然后\(FFT\),就可以知道在这一段里面这个字符匹配上了多少个,然后把每个字符求个和,检查是否恰好匹配了\(|T|\)个。
通配符此时并不需要考虑。
时间复杂度\(O(26nlogn)\)
当然,如果您真的这么写了,那么肯定\(T\)飞了。\(FFT\)自带巨大常数。对于字符集很小的时候上述方法是可行的,字符集很大的时候就不能这么做了。
所以我们考虑如何只做一遍\(FFT\)。(没有通配符的情况下)
每个字符当然不能动了,所以把他们对应成数字。如果匹配上了怎么办?那就是上下两个对应的数字相等,可以考虑作差。但是又发现作差可能会使得几个正数和几个负数相加变成\(0\),所以考虑平方。
所以,我们定义每个位置的函数值\(f(x)=\sum_{i=1}^{|T|}(S[x+i-1]-T[i])^2\)
将\(T\)串反转,平方项拆开之后,就变成了两个卷积+一个常数项的形式,直接\(FFT\)然后求和,检查最后的函数值是否为\(0\)就行了。
现在有了通配符,我们不妨令\(a..z\)对应的数字为\(1..26\)。通配符无论给它一个什么数字,它和上面的字符的差的平方一定不为\(0\)。
所以我们换种方法考虑,把通配符对应的数字设为\(0\),但是这样差的平方不是\(0\),所以我们就在上面那个式子后面乘上一个\(T[i]\)的权值,如果此时是通配符就会乘\(0\),从而也变成了\(0\)。这样一来,原来的式子就变成了\(f(x)=\sum_{i=1}^{|T|}(S[x+i-1]-T[i])^2T[i]\)
拆开后是两个卷积+一个常数项的形式,\(FFT\)即可。
时间复杂度\(O(nlogn)\)
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define ll long long
#define RG register
#define MAX 333333
const double Pi=acos(-1);
struct Complex{double a,b;}A1[MAX],B1[MAX],A2[MAX],B2[MAX],W[MAX],F[MAX];
Complex operator+(Complex a,Complex b){return (Complex){a.a+b.a,a.b+b.b};}
Complex operator-(Complex a,Complex b){return (Complex){a.a-b.a,a.b-b.b};}
Complex operator*(Complex a,Complex b){return (Complex){a.a*b.a-a.b*b.b,a.a*b.b+a.b*b.a};}
int n,m,r[MAX],N,Z;
int pos[MAX],ans,l;
char S[MAX],T[MAX];
void FFT(Complex *P,int opt)
{
for(int i=1;i<N;++i)if(i<r[i])swap(P[i],P[r[i]]);
for(int i=1;i<N;i<<=1)
for(int p=i<<1,j=0;j<N;j+=p)
for(int k=0;k<i;++k)
{
Complex w=(Complex){W[N/i*k].a,W[N/i*k].b*opt};
Complex X=P[j+k],Y=w*P[j+k+i];
P[j+k]=X+Y;P[i+j+k]=X-Y;
}
if(opt==-1)for(int i=0;i<N;++i)P[i].a/=N;
}
int main()
{
scanf("%s",S);scanf("%s",T);
n=strlen(S);m=strlen(T);
for(N=1;N<=(n+m-2);N<<=1)++l;
for(int i=0;i<N;++i)r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
for(int i=1;i<N;i<<=1)
for(int k=0;k<i;++k)W[N/i*k]=(Complex){cos(k*Pi/i),sin(k*Pi/i)};
for(int i=0;i<n;++i)A1[i].a=(S[i]-96)*(S[i]-96),A2[i].a=2*(S[i]-96);
for(int i=0;i<m;++i)
{
int x=((T[m-i-1]=='?')?0:(T[m-i-1]-96));
B1[i].a=x;B2[i].a=x*x;Z+=x*x*x;
}
FFT(A1,1);FFT(B1,1);FFT(A2,1);FFT(B2,1);
for(int i=0;i<N;++i)
F[i]=A1[i]*B1[i]-A2[i]*B2[i];
FFT(F,-1);
for(int i=m-1;i<n;++i)
if((int)(F[i].a+0.5+Z)==0)pos[++ans]=i-m+1;
printf("%d\n",ans);
for(int i=1;i<=ans;++i)printf("%d\n",pos[i]);
return 0;
}
【BZOJ4503】两个串(FFT)的更多相关文章
- bzoj4503: 两个串 bitset
目录 题目链接 题解 代码 题目链接 bzoj4503: 两个串 题解 暴一发bitset f[i][j] 表示 S[1..i] 是否有个后缀能匹配 T[1..j] 那么假设 S[i+1] 能匹配 T ...
- 【BZOJ4503】两个串 FFT
[BZOJ4503]两个串 Description 兔子们在玩两个串的游戏.给定两个字符串S和T,兔子们想知道T在S中出现了几次, 分别在哪些位置出现.注意T中可能有“?”字符,这个字符可以匹配任何字 ...
- 【bzoj4259/bzoj4503】残缺的字符串/两个串 FFT
bzoj4259 题目描述 很久很久以前,在你刚刚学习字符串匹配的时候,有两个仅包含小写字母的字符串A和B,其中A串长度为m,B串长度为n.可当你现在再次碰到这两个串时,这两个串已经老化了,每个串都有 ...
- BZOJ4259: 残缺的字符串 & BZOJ4503: 两个串
[传送门:BZOJ4259&BZOJ4503] 简要题意: 给出两个字符串,第一个串长度为m,第二个串长度为n,字符串中如果有*字符,则代表当前位置可以匹配任何字符 求出第一个字符串在第二个字 ...
- BZOJ 4503: 两个串 [FFT]
4503: 两个串 题意:兔子们在玩两个串的游戏.给定两个只含小写字母的字符串S和T,兔子们想知道T在S中出现了几次, 分别在哪些位置出现.注意T中可能有"?"字符,这个字符可以匹 ...
- BZOJ4503: 两个串
Description 兔子们在玩两个串的游戏.给定两个字符串S和T,兔子们想知道T在S中出现了几次, 分别在哪些位置出现.注意T中可能有“?”字符,这个字符可以匹配任何字符. Input 两行两个字 ...
- BZOJ4503 两个串 多项式 FFT
题目传送门 - BZOJ4503 题意概括 给定两个字符串S和T,回答T在S中出现了几次,在哪些位置出现.注意T中可能有?字符,可以匹配任何字符. 题解 首先,假装你已经知道了这是一道$FFT$题. ...
- 2019.02.06 bzoj4503: 两个串(fft)
传送门 题意简述:给两个字符串s,ts,ts,t,ttt中可能有通配符,问ttt在sss出现的次数和所有位置. 思路:一道很熟悉的题,跟bzoj4259bzoj4259bzoj4259差不多的. 然后 ...
- 【bzoj4503】 两个串 FFT
$FFT$套路题(然而我看错题了) 我们考虑化一下式子. 设当前比较的两个部分为$S[i....i+|T|-1]$和$T[0....|T|-1]$. 我们对串$T$中出现问号的位置全部赋值为$0$. ...
- BZOJ4503 两个串 【fft】
题目链接 BZOJ4503 题解 水水题. 和残缺的字符串那题几乎是一样的 同样转化为多项式 同样TLE 同样要手写一下复数才A #include<algorithm> #include& ...
随机推荐
- 重启 IIS7 应用或者应用程序池的批处理bat
重启应用 本地: ctrl+r->iisreset -stop ctrl+r->iisreset -start ctrl+r->iisreset 远程(假如远程机器地址为10.5.6 ...
- Centos7 64位 -- glibc-2.29 编译升级方法(已成功)
某软件出现漏洞,需要升级解决(忘了哪个)结果提示glibc版本过低. 懵懂无知的我以为glibc想其他软件一样编译升级一下就好.. 结果? 重装系统! 说真的,如非必要(或学习),请勿升级 glibc ...
- Qt实现软件自动更新的一种简单方法
前言 最近在学习Qt开发上位机,想实现一个检查更新的功能,网上搜索了一大圈,发现实现过程都很复杂,关键是代码看不懂,所以就自己开发一种简单的方式来实现.实现效果如下: 点击"检查更新&quo ...
- 第七次Scrum meeting
第七次Scrum meeting 任务及完成度: 成员 12.26 12.27 陈谋 任务1040:完成stackoverflow的数据处理后的json处理(98%) 任务1114-1:完成对网页数 ...
- Linux内核分析— —创建新进程的过程
分析Linux内核创建一个新进程的过程 实验过程 要求:使用gdb跟踪分析一个fork系统调用内核处理函数sys_clone ,验证对Linux系统创建一个新进程的理解,推荐在实验楼Linux虚拟机环 ...
- 《Linux课本》读书笔记 第四章
- 作业七:Linux内核如何装载和启动一个可执行程序
作业七:Linux内核如何装载和启动一个可执行程序 一.编译链接的过程和ELF可执行文件格式 可执行文件的创建——预处理.编译和链接 在object文件中有三种主要的类型. 一个可重定位(reloca ...
- 《软件工程》小组团队项目-小学生四则运算APP(First Sprint)
<软件工程>团队项目我们小组选择了小学生四则运算APP,在上学期原有的项目基础上进行更新升级.(自我感觉我们团队上学期的小学生四则运算APP是较为成功且实用的,不过这学期学习到了新的知识, ...
- vue的使用1
Vue.$set(object, key, value); <!-- Alt + C --> <input @keyup.alt.="clear"> < ...
- FICO基础知识(一)
GL – 总账 (General Ledger) 总帐核算的中心任务是提供外部会计及其所涉及帐户的概貌. 总账会计主要用途:根据不同的会计准则(如欧洲的 IAS, 美国的GAAP, 中国国家会计准则) ...