题目链接

\(Description\)

给定两个字符串S和T,求T在S中出现了几次,以及分别在哪些位置出现。T中可能有'?'字符,这个字符可以匹配任何字符。

\(|S|,|T|\leq 10^5\)。

\(Solution\)

FFT:

https://www.cnblogs.com/cjyyb/p/8798446.html

显然我们可以同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)的更多相关文章

  1. BZOJ 4503: 两个串 [FFT]

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

  2. bzoj 4503 两个串——FFT

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4503 翻转T,就变成卷积.要想想怎么判断. 因为卷积是乘积求和,又想到相等的话相减为0,所以 ...

  3. bzoj 4503 两个串 —— FFT

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4503 推式子即可: 不知怎的调了那么久,应该是很清晰的. 代码如下: #include< ...

  4. BZOJ 4503 两个串 ——FFT

    [题目分析] 定义两个字符之间的距离为 (ai-bi)^2*ai*bi 如果能够匹配,从i到i+m的位置的和一定为0 但这和暴力没有什么区别. 发现把b字符串反过来就可以卷积用FFT了. 听说KMP+ ...

  5. bzoj 4503 两个串

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

  6. 【刷题】BZOJ 4503 两个串

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

  7. BZOJ 4503 两个串(FFT)

    [题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=4503 [题目大意] 给出S串和T串,计算T在S中出现次数,T中有通配符'?'. [题解 ...

  8. bzoj 4503 两个串 快速傅里叶变换FFT

    题目大意: 给定两个\((length \leq 10^5)\)的字符串,问第二个串在第一个串中出现了多少次.并且第二个串中含有单字符通配符. 题解: 首先我们从kmp的角度去考虑 这道题从字符串数据 ...

  9. bzoj 4503: 两个串【脑洞+FFT】

    真实脑洞题 因为通配符所以导致t串实际有指数级别个,任何字符串相关算法都没有用 考虑一个新的匹配方法:设a串(模板串)长为n,从m串的i位置开始匹配:\( \sum_{i=0}^{n-1}(a[j]- ...

随机推荐

  1. centos之mysql安装配置使用

    安装:  这里只安装得时mysql5.1,如果想安装5.6,看这里http://www.cnblogs.com/xuange306/p/8243859.html yum install -y mysq ...

  2. swagger2访问url

    swagger : http://localhost:8080/swagger/index.html springboot中的swagger:http://localhost:8080/swagger ...

  3. 论文阅读笔记十八:ENet: A Deep Neural Network Architecture for Real-Time Semantic Segmentation(CVPR2016)

    论文源址:https://arxiv.org/abs/1606.02147 tensorflow github: https://github.com/kwotsin/TensorFlow-ENet ...

  4. Lottie 动画

    #### 三方框架之Lotti使用Lottie 的使用 1.添加 Gradle 依赖 dependencies { compile 'com.airbnb.android:lottie:1.5.3'} ...

  5. java数据

    因为曾经干了啥事儿,才印象特别深刻. 将byte存入String的后果 String res = ""; res += (char) 0xc3; byte[] bytes = re ...

  6. Atom插件下载失败解决办法

    转自:http://www.cnblogs.com/20145221GQ/p/5334762.html#正题 一般方法(Atom自动安装) 打开Atom >> Packages >& ...

  7. 435. Non-overlapping Intervals

    Given a collection of intervals, find the minimum number of intervals you need to remove to make the ...

  8. EF大数据批量处理 EntityFrameWork下增加扩展方法

    为EF操作方法添加扩展方法 BulkInsert 大致设计方式为 通过当前DbContext 获取当前连接字符串,调用连接字符串获取当前实体的所有字段及字段属性,映射到DataTable中 在调用Sy ...

  9. Codeforces 1045C Hyperspace Highways (看题解) 圆方树

    学了一下圆方树, 好神奇的东西呀. #include<bits/stdc++.h> #define LL long long #define fi first #define se sec ...

  10. union和union all的区别(面试常考)

    Union:对两个结果集进行并集操作,不包括重复行,同时进行默认规则的排序: Union All:对两个结果集进行并集操作,包括重复行,不进行排序: Union因为要进行重复值扫描,所以效率低.如果合 ...