menu ZigZagK的博客

正在努力加载中QAQ

[思维+FFT]BZOJ4259【残缺的字符串】题解
apps BZOJ
local_offer 查看标签
comment 0 条评论
remove_red_eye 15 次访问

题目概述

有两个串 $A,B$ ,有些位置是通配符,求 $A$ 可以在 $B$ 的哪些位置匹配。

解题报告

早就听说了这题,今天来填坑。判断两个字符串相等可以用式子:$\sum_{i=0}^{len}(A_i-B_i)^2=0$ ,但是有通配符,所以可以把通配符给 $0$ ,然后式子变成这样:$\sum_{i=0}^{len}(A_i-B_i)^2A_iB_i=0$ 。

对于 $B$ 的每个开头 $i$ ,展开来是这样的:

$$ sum_i=\sum_{j=0}^{|A|}A_j^3B_{i+j}-2A_j^2B_{i+j}^2+A_iB_{i+j}^3\\ sum_i=\sum_{k-j=i}A_j^3B_k-2A_j^2B_k^2+A_jB_k^3\\ sum_{i+|B|}=\sum_{k+j=i+|B|}A_{|B|-j}^3B_k-2A_{|B|-j}^{2}B_k^2+A_{|B|-j}B_k^3 $$

这是?三个卷积?把 $A$ 补成 $B$ 的长度,然后套三次FFT就行啦。

示例程序

#include<cmath>
#include<cstdio>
#include<algorithm>
#define fr first
#define sc second
#define mp make_pair
using namespace std;
typedef long long LL;typedef double DB;typedef pair<DB,DB> CN;
const int maxn=300000,maxm=1<<20;const DB pi=acos(-1);

int A,B,n,m,rev[maxm+5];char a[maxn+5],b[maxn+5];CN f[maxm+5],g[maxm+5];int ans;LL sum[maxn+5];

inline CN operator + (const CN &a,const CN &b) {return mp(a.fr+b.fr,a.sc+b.sc);}
inline CN operator - (const CN &a,const CN &b) {return mp(a.fr-b.fr,a.sc-b.sc);}
inline CN operator * (const CN &a,const CN &b) {return mp(a.fr*b.fr-a.sc*b.sc,a.fr*b.sc+a.sc*b.fr);}
inline void Rev(int n,int len) {for (int i=1;i<n;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<len-1);}
inline void FFT(CN *a,int n,int f){
    for (int i=0;i<n;i++) if (i<rev[i]) swap(a[i],a[rev[i]]);
    for (int k=1;k<n;k<<=1){
        CN wn=mp(cos(pi/k),f*sin(pi/k)),w=mp(1,0),x,y;
        for (int i=0;i<n;i+=k<<1,w=mp(1,0))
            for (int j=0;j<k;j++,w=w*wn)
                x=a[i+j],y=a[i+j+k]*w,a[i+j]=x+y,a[i+j+k]=x-y;
    }if (f<0) for (int i=0;i<n;i++) a[i].fr/=n;
}
int main(){
    scanf("%d%d",&A,&B);A--;B--;n=B;scanf("%s%s",a,b);
    for (int i=0;i<=A;i++) if (a[i]!='*') a[i]=a[i]-'a'+1;for (int i=0;i<=B;i++) if (b[i]!='*') b[i]=b[i]-'a'+1;
    int len=0;for (m=1;m<=(n<<1);m<<=1) len++;Rev(m,len);

    for (int i=0;i<=n;i++) if (a[n-i]!='*') f[i]=mp(a[n-i]*a[n-i]*a[n-i],0); else f[i]=mp(0,0);
    for (int i=0;i<=n;i++) if (b[i]!='*') g[i]=mp(b[i],0); else g[i]=mp(0,0);
    FFT(f,m,1);FFT(g,m,1);for (int i=0;i<m;i++) f[i]=f[i]*g[i];FFT(f,m,-1);
    for (int i=0;i<=n;i++) sum[i]+=LL(f[i+n].fr+0.5);for (int i=0;i<m;i++) f[i]=g[i]=mp(0,0);

    for (int i=0;i<=n;i++) if (a[n-i]!='*') f[i]=mp(a[n-i]*a[n-i],0); else f[i]=mp(0,0);
    for (int i=0;i<=n;i++) if (b[i]!='*') g[i]=mp(b[i]*b[i],0); else g[i]=mp(0,0);
    FFT(f,m,1);FFT(g,m,1);for (int i=0;i<m;i++) f[i]=f[i]*g[i];FFT(f,m,-1);
    for (int i=0;i<=n;i++) sum[i]-=LL(f[i+n].fr+0.5)<<1;for (int i=0;i<m;i++) f[i]=g[i]=mp(0,0);

    for (int i=0;i<=n;i++) if (a[n-i]!='*') f[i]=mp(a[n-i],0); else f[i]=mp(0,0);
    for (int i=0;i<=n;i++) if (b[i]!='*') g[i]=mp(b[i]*b[i]*b[i],0); else g[i]=mp(0,0);
    FFT(f,m,1);FFT(g,m,1);for (int i=0;i<m;i++) f[i]=f[i]*g[i];FFT(f,m,-1);

    for (int i=0;i<=n;i++) sum[i]+=LL(f[i+n].fr+0.5);for (int i=0;i<=B-A;i++) ans+=!sum[i];printf("%d\n",ans);
    for (int i=0,fst=true;i<=B-A;i++) if (!sum[i]) fst?fst=false:putchar(' '),printf("%d",i+1);puts("");return 0;
}
版权声明:本博客所有文章除特别声明外,均采用 CC BY 4.0 CN协议 许可协议。转载请注明出处!
名称不能为空
邮箱不能为空,请填写正确格式
网址请用http://或https://开头
评论不能为空
资瓷Markdown和LaTex数学公式
keyboard_arrow_up