【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)的更多相关文章

  1. bzoj4503: 两个串 bitset

    目录 题目链接 题解 代码 题目链接 bzoj4503: 两个串 题解 暴一发bitset f[i][j] 表示 S[1..i] 是否有个后缀能匹配 T[1..j] 那么假设 S[i+1] 能匹配 T ...

  2. 【BZOJ4503】两个串 FFT

    [BZOJ4503]两个串 Description 兔子们在玩两个串的游戏.给定两个字符串S和T,兔子们想知道T在S中出现了几次, 分别在哪些位置出现.注意T中可能有“?”字符,这个字符可以匹配任何字 ...

  3. 【bzoj4259/bzoj4503】残缺的字符串/两个串 FFT

    bzoj4259 题目描述 很久很久以前,在你刚刚学习字符串匹配的时候,有两个仅包含小写字母的字符串A和B,其中A串长度为m,B串长度为n.可当你现在再次碰到这两个串时,这两个串已经老化了,每个串都有 ...

  4. BZOJ4259: 残缺的字符串 & BZOJ4503: 两个串

    [传送门:BZOJ4259&BZOJ4503] 简要题意: 给出两个字符串,第一个串长度为m,第二个串长度为n,字符串中如果有*字符,则代表当前位置可以匹配任何字符 求出第一个字符串在第二个字 ...

  5. BZOJ 4503: 两个串 [FFT]

    4503: 两个串 题意:兔子们在玩两个串的游戏.给定两个只含小写字母的字符串S和T,兔子们想知道T在S中出现了几次, 分别在哪些位置出现.注意T中可能有"?"字符,这个字符可以匹 ...

  6. BZOJ4503: 两个串

    Description 兔子们在玩两个串的游戏.给定两个字符串S和T,兔子们想知道T在S中出现了几次, 分别在哪些位置出现.注意T中可能有“?”字符,这个字符可以匹配任何字符. Input 两行两个字 ...

  7. BZOJ4503 两个串 多项式 FFT

    题目传送门 - BZOJ4503 题意概括 给定两个字符串S和T,回答T在S中出现了几次,在哪些位置出现.注意T中可能有?字符,可以匹配任何字符. 题解 首先,假装你已经知道了这是一道$FFT$题. ...

  8. 2019.02.06 bzoj4503: 两个串(fft)

    传送门 题意简述:给两个字符串s,ts,ts,t,ttt中可能有通配符,问ttt在sss出现的次数和所有位置. 思路:一道很熟悉的题,跟bzoj4259bzoj4259bzoj4259差不多的. 然后 ...

  9. 【bzoj4503】 两个串 FFT

    $FFT$套路题(然而我看错题了) 我们考虑化一下式子. 设当前比较的两个部分为$S[i....i+|T|-1]$和$T[0....|T|-1]$. 我们对串$T$中出现问号的位置全部赋值为$0$. ...

  10. BZOJ4503 两个串 【fft】

    题目链接 BZOJ4503 题解 水水题. 和残缺的字符串那题几乎是一样的 同样转化为多项式 同样TLE 同样要手写一下复数才A #include<algorithm> #include& ...

随机推荐

  1. 使用HashSet<>去除重复元素的集合

    比如,某一个阵列中,有重复的元素,我们想去除重复的,保留一个.HashSet<T>含不重复项的无序列表,从MSDN网上了解到,这集合基于散列值,插入元素的操作非常快. 你可以写一个方法: ...

  2. linux awk 内置函数实例

    awk内置函数,主要分4种:算数函数.字符串函数.时间函数.一般函数 一.算术函数 以下算术函数执行与 C 语言中名称相同的子例程相同的操作: 函数名 说明 atan2( y, x ) 返回 y/x ...

  3. cp 命令有坑

    cp 是个很常用的命令, 基本语法为  cp -v  a   b  把文件a 复制为文件b(-v为显示做了什么,这是非常安全的做法,建议新手添加此参数) 参数说明: -a:此选项通常在复制目录时使用, ...

  4. 硬编码转换单位||vue

    //测试单位 formatUnit:function (id) { var val; switch(id){ case 4: return val="圈" break; } } / ...

  5. 读书笔记(chapter17)

    设备类型:在所有Unix系统中为了统一普通设备的操作所采用的分类 模块:Linux内核中用于按需加载和卸载目标码的机制 内核对象:内核数据结构中支持面对对象的简单操作,还支持维护对象之间的父子关系 1 ...

  6. android计算器

    一:引言    目前手机可以说是普及率非常高的电子设备了,由于其便于携带,使用方便,资费适中等等原因,现在手机已经在一定程度开始代替固定电话的通话功能,以及一些原来电脑软件上的功能了.手机上的软件也随 ...

  7. android 活动的生命周期

    掌握活动的生命周期非常重要,因为一个正常的android应用,会有很多的活动,如何在这些活动之间进行切换.数据的交互等,就经常会用到活动的生命周期这一块的知识.可以说,只要掌握了活动的生命周期,才能更 ...

  8. 【软件工程Ⅱ】作业二 |分布式版本控制系统Git的安装与使用

    本次作业的要去来自于:https://edu.cnblogs.com/campus/gzcc/GZCC-16SE2/homework/2097 远程库地址:https://github.com/Mol ...

  9. PHP文件下载功能实现

    客户端的浏览器通过HTTP协议可以实现文件下载: 方法一: 能提供用户下载的最简单的方法就是使用一个<a></a>标签,比如在页面中添加这么一行代码 <a href=&q ...

  10. Classification Truth Table

    在机器学习中对于分类结果的描述,一般有四种:true positive, true negative, false positive 和 false negative. Precision, Reca ...