BZOJ.4503.两个串(FFT/bitset)
\(Description\)
给定两个字符串S和T,求T在S中出现了几次,以及分别在哪些位置出现。T中可能有'?'字符,这个字符可以匹配任何字符。
\(|S|,|T|\leq 10^5\)。
\(Solution\)
FFT:
显然我们可以同CF528D一样枚举\(26\)个字符然后跑\(FFT\)。但字符集太大了,复杂度是\(O(26n\log n)\),以\(FFT\)的常数肯定GG。(然而CF上一道题\(n\)更大但\(36n\log n\)的\(FFT\)还跑的非常轻松,这就是差距么...)
考虑没有通配符时能怎么做(不考虑\(KMP\))。
把每个字符\(a\sim z\)映射到\(1\sim26\),那么\(S_i,T_i\)匹配了就是\(S_i=T_i\)。考虑\(S,T\)做差。但是每个位置需要有\(|T|\)个值求和来表示匹配\(T\)串的情况,就算不匹配正负相加也可能变成\(0\)。
所以考虑平方,即若\(f(x)=\sum_{i=0}^{|T|-1}(S_{x+i}-T_i)^2=0\),那么\(x\)位置匹配了\(T\)。
把平方和拆开,就可以得到两个常数项和一个\(\sum_{i=0}^{|T|-1}S_{x+i}T_i\),把\(T\)反转后就可以\(FFT\)了。
如果有通配符呢?考虑如果\(T_i\)是通配符,怎么让它不产生影响,也就是贡献是\(0\)。
那么令通配符\(T_i=0\),外面再乘个\(T_i\)就可以了。即$$\begin{aligned}f(x)&=\sum_{i=0}{|T|-1}(S_{x+i}-T_i)2T_i\&=\sum_{i=0}{|T|-1}S_{x+i}2T_i-2\sum_{i=0}{|T|-1}S_{x+i}T_i2+\sum_{i=0}{|T|-1}T_i3\end{aligned}$$
第三部分是个常数,前两部分可以分别\(FFT\)求出来。
//22280kb 3076ms(这慢的...)
#include <cmath>
#include <cctype>
#include <cstdio>
#include <cstring>
#include <algorithm>
//#define gc() getchar()
#define MAXIN 1000000
#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
typedef long long LL;
const int N=(1<<18)+5;
const double PI=acos(-1);
int S[N],T[N],rev[N];
char IN[MAXIN],*SS=IN,*TT=IN;
struct Complex
{
double x,y;
Complex(double x=0,double y=0):x(x),y(y) {}
Complex operator +(const Complex &a) {return Complex(x+a.x, y+a.y);}
Complex operator -(const Complex &a) {return Complex(x-a.x, y-a.y);}
Complex operator *(const Complex &a) {return Complex(x*a.x-y*a.y, x*a.y+y*a.x);}
}A[N],B[N],C[N],D[N];
void FFT(Complex *a,int lim,int opt)
{
for(int i=1; i<lim; ++i) if(i<rev[i]) std::swap(a[i],a[rev[i]]);
for(int i=2; i<=lim; i<<=1)
{
int mid=i>>1; Complex Wn(cos(PI/mid),opt*sin(PI/mid));
for(int j=0; j<lim; j+=i)
{
Complex w(1,0),t;
for(int k=j; k<j+mid; ++k,w=w*Wn)
a[k+mid]=a[k]-(t=a[k+mid]*w), a[k]=a[k]+t;
}
}
if(opt==-1) for(int i=0; i<lim; ++i) a[i].x/=lim;
}
int main()
{
static int pos[N];
register char c;
int n=0; while(isalpha(c=gc())) S[n++]=c-96;
int m=0; while(isalpha(c=gc())||c=='?') T[m++]=c=='?'?0:c-96;
std::reverse(T,T+m);//!
for(int i=0; i<n; ++i) A[i]=Complex(S[i]*S[i],0),C[i]=Complex(S[i]<<1,0);
int sumT=0;
for(int i=0,t; i<m; ++i) B[i]=Complex(t=T[i],0),D[i]=Complex(t*t,0),sumT+=t*t*t;
int lim=1,l=-1;
while(lim<=n+m-2) lim<<=1,++l;//n+m-2就可以了...~~有点想不通...~~n-1次和m-1次咯
for(int i=1; i<lim; ++i) rev[i]=(rev[i>>1]>>1)|((i&1)<<l);
FFT(A,lim,1), FFT(B,lim,1), FFT(C,lim,1), FFT(D,lim,1);
for(int i=0; i<lim; ++i) A[i]=A[i]*B[i]-C[i]*D[i];//可以直接在这里计算然后只IDFT一次啊(反正都是多项式)
FFT(A,lim,-1);
int ans=0;
for(int i=0; i<=n-m; ++i) if(!(int)(A[m+i-1].x+0.5+sumT)) pos[++ans]=i;
printf("%d\n",ans);
for(int i=1; i<=ans; printf("%d\n",pos[i++]));
return 0;
}
bitset:
可以用bitset
暴力匹配。
维护bitset<N> Ans
表示每个位置能否匹配\(T\)串。枚举\(T\)中的字符\(T_j\),维护\(Ans_i\)是否能够匹配\(T_j\),也就是\(S_{i+j}\)处是否是\(T_j\)。
可以用\(26\)个bitset<N> Pos[26]
得到每种字符在\(S\)的哪些位置上出现过(出现过的位置设为\(1\))。
然后枚举\(T_j\)(\(j\in[0,T|\))的时候,\(Ans_i\)每次都与上\(Pos_{T_j}\)右移\(j\)位就可以了。(若是通配符就不用再与了)(注意是右移)
最后若\(Ans_i\)还是\(1\),则\(i\)位置匹配\(T\)串。
复杂度\(O(\frac{nm}{w})\)。
//2744kb 1372ms
#include <cctype>
#include <cstdio>
#include <bitset>
#include <algorithm>
//#define gc() getchar()
#define MAXIN 1000000
#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
const int N=1e5+3;
char S[N],T[N];
std::bitset<N> Ans,pos[26];
char IN[MAXIN],*SS=IN,*TT=IN;
int main()
{
static int p[N];
register char c;
int n=0; while(isalpha(S[n]=gc())) ++n;//会有个换行符啊=_= 所以while的时候n先别加。
int m=0; while(isalpha(T[m]=gc())||T[m]=='?') ++m;
for(int i=0; i<n; ++i) pos[S[i]-97].set(i);
Ans.set();
for(int i=0; i<m; ++i) if(T[i]!='?') Ans&=pos[T[i]-97]>>i;
int ans=0;
for(int i=0; i<=n-m; ++i) if(Ans[i]==1) p[++ans]=i;
printf("%d\n",ans);
for(int i=1; i<=ans; printf("%d\n",p[i++]));
return 0;
}
BZOJ.4503.两个串(FFT/bitset)的更多相关文章
- BZOJ 4503: 两个串 [FFT]
4503: 两个串 题意:兔子们在玩两个串的游戏.给定两个只含小写字母的字符串S和T,兔子们想知道T在S中出现了几次, 分别在哪些位置出现.注意T中可能有"?"字符,这个字符可以匹 ...
- bzoj 4503 两个串——FFT
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4503 翻转T,就变成卷积.要想想怎么判断. 因为卷积是乘积求和,又想到相等的话相减为0,所以 ...
- bzoj 4503 两个串 —— FFT
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4503 推式子即可: 不知怎的调了那么久,应该是很清晰的. 代码如下: #include< ...
- BZOJ 4503 两个串 ——FFT
[题目分析] 定义两个字符之间的距离为 (ai-bi)^2*ai*bi 如果能够匹配,从i到i+m的位置的和一定为0 但这和暴力没有什么区别. 发现把b字符串反过来就可以卷积用FFT了. 听说KMP+ ...
- bzoj 4503 两个串
Description 兔子们在玩两个串的游戏.给定两个字符串S和T,兔子们想知道T在S中出现了几次, 分别在哪些位置出现.注意T中可能有“?”字符,这个字符可以匹配任何字符. Input 两行两个字 ...
- 【刷题】BZOJ 4503 两个串
Description 兔子们在玩两个串的游戏.给定两个字符串S和T,兔子们想知道T在S中出现了几次, 分别在哪些位置出现.注意T中可能有"?"字符,这个字符可以匹配任何字符. I ...
- BZOJ 4503 两个串(FFT)
[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=4503 [题目大意] 给出S串和T串,计算T在S中出现次数,T中有通配符'?'. [题解 ...
- bzoj 4503 两个串 快速傅里叶变换FFT
题目大意: 给定两个\((length \leq 10^5)\)的字符串,问第二个串在第一个串中出现了多少次.并且第二个串中含有单字符通配符. 题解: 首先我们从kmp的角度去考虑 这道题从字符串数据 ...
- bzoj 4503: 两个串【脑洞+FFT】
真实脑洞题 因为通配符所以导致t串实际有指数级别个,任何字符串相关算法都没有用 考虑一个新的匹配方法:设a串(模板串)长为n,从m串的i位置开始匹配:\( \sum_{i=0}^{n-1}(a[j]- ...
随机推荐
- java子类对象和成员变量的隐写&方法重写
1.子类继承的方法只能操作子类继承和隐藏的成员变量名字类新定义的方法可以操作子类继承和子类新生命的成员变量,但是无法操作子类隐藏的成员变量(需要适用super关键字操作子类隐藏的成员变量.) publ ...
- python+selenium十四:xpath和contains模糊匹配
xpath可以以标签定位,也可以@任意属性: 如:以input标签定位:driver.find_element_by_xpath("//input[@id='kw']") 如:@t ...
- PXE+HTTP+TFTP+Kickstart实现无人值守部署centos6.10
在联网的状态下安装所需软件: Shell> yum install dhcp httpd tftp-server xinetd syslinux system-config-kickstart ...
- Wowza 相关
下载: 1.https://www.wowza.com/pricing/installer 安装: https://www.wowza.com/docs/how-to-install-and-conf ...
- openmp查看最大线程数量
CMakeLists.txt cmake_minimum_required(VERSION 2.8) project(omp_test) find_package(OpenMP REQUIRED) i ...
- Javascript中函数提升和变量提升
词法分析 词法分析方法: js运行前有一个类似编译的过程即词法分析,词法分析主要有三个步骤: 分析参数 再分析变量的声明 分析函数说明 具体步骤如下: 函数在运行的瞬间,生成一个活动对象(Active ...
- C#学习-const和readonly
const是表示为常量的关键字,一旦赋值就不能改变了.是程序编译时候CLR就将const的值编译到IL代码中了. readonly也是常量的关键的字: 所以,有了这两个关键字的比较.readonly肯 ...
- react native初始化项目
打开命令行窗口,进入我们想要创建项目的父目录,输入命令: npm install -g yarn react-native-cli react-native init 项目名 进入新建的项目目录,执行 ...
- 2018项目UML设计-课堂实战
1. 团队信息 队名:小白吃队 成员: 卢泽明 031602328 蔡文斌 031602301 葛亮 031602617 刘浩 031602423 张扬 031602345 李泓 031602321 ...
- [转]MySQL忘记密码的正确解决方法
http://database.51cto.com/art/201005/201986.htm 以下的文章主要介绍的是MySQL忘记密码的正确解决方法,在实际操作中如果你忘记MySQL密码是一件很头痛 ...