bzoj4259 残缺的字符串

题目描述

给定 S , T S,T S,T ,其中 ∣ T ∣ < ∣ S ∣ |T|<|S| T<S ,问 T T T S S S 中出现了几次, ∗ * 可以充当任何字母

数据范围

∣ T ∣ < ∣ S ∣ ≤ 3 × 1 0 5 |T|<|S| \le 3 \times 10^5 T<S3×105

题解

如果没有 ∗ * 的话,那可以想一种构造方法,使我们快速判定 S S S 中的某个子串是不是和 T T T 相同。

假设 S S S 从下标 k k k 开始,也就是快速判断 ∑ [ S k + i − 1 = T i ] \sum [S_{k+i-1}=T_i] [Sk+i1=Ti] 是不是等于 ∣ T ∣ |T| T

那我们可以换种思路,如果 S k + i − 1 = T i S_{k+i-1}=T_i Sk+i1=Ti ,那么 S k − i + 1 − T i = 0 S_{k-i+1}-T_i=0 Ski+1Ti=0

所以我们可以判断 ∑ ( S k + i − 1 − T i ) 2 \sum (S_{k+i-1}-T_i)^2 (Sk+i1Ti)2 是不是等于 0 0 0

然后我们发现他们下标的差是固定的,所以我们可以把 T T T 翻转,然后把式子展开,我们就得到了卷积的形式。

现在有 ∗ * 的限制,那只需要让是 ∗ * 的那一位的权值设成 0 0 0 ,我们判断 ∑ ( S k + i − 1 − T ∣ T ∣ − i + 1 ) 2 × S k + i − 1 × T ∣ T ∣ − i + 1 \sum (S_{k+i-1}-T_{|T|-i+1})^2 \times S_{k+i-1} \times T_{|T|-i+1} (Sk+i1TTi+1)2×Sk+i1×TTi+1 是不是等于 0 0 0 即可。

效率: O ( n l o g n ) O(nlogn) O(nlogn)

代码

#include <bits/stdc++.h>
#define db double
using namespace std;
const int N=12e5+5;
const db PI=acos(-1);
char S[N],T[N];
int n,m,t=1,p,r[N],f[N],s;
struct O{db r,i;}a[N],b[N],c[N],d[N],e[N],h[N];
O operator + (O A,O B){
    return (O){A.r+B.r,A.i+B.i};
}
O operator - (O A,O B){
    return (O){A.r-B.r,A.i-B.i};
}
O operator * (O A,O B){
    return (O){A.r*B.r-A.i*B.i,A.r*B.i+A.i*B.r};
}
void FFt(O *g,int o){
    for (int i=0;i<t;i++)
        if (i<r[i]) swap(g[i],g[r[i]]);
    for (int i=1;i<t;i<<=1){
        O wn=(O){cos(PI/i),sin(PI/i)*o};
        for (int j=0;j<t;j+=(i<<1)){
            O w=(O){1,0},x,y;
            for (int k=0;k<i;k++,w=w*wn)
                x=g[j+k],y=g[i+j+k]*w,
                g[j+k]=x+y,g[i+j+k]=x-y;
        }
    }
    if (!~o) for (int i=0;i<t;i++) g[i].r/=t;
}
int main(){
    scanf("%d%d%s%s",&m,&n,T,S);
    for (;t<n+m;t<<=1,p++);
    for (int i=0;i<t;i++)
        r[i]=(r[i>>1]>>1)|((i&1)<<(p-1));
    for (int x,i=0;i<n;i++) if (S[i]!='*')
        x=S[i]-96,a[i].r=x*x*x,b[i].r=2*x*x,e[i].r=x;
    for (int x,i=0;i<m;i++) if (T[m-i-1]!='*')
        x=T[m-i-1]-96,c[i].r=x,d[i].r=x*x,h[i].r=x*x*x;
    FFt(a,1);FFt(b,1);FFt(c,1);FFt(d,1);FFt(e,1);FFt(h,1);
    for (int i=0;i<t;i++)
        a[i]=a[i]*c[i]-b[i]*d[i]+e[i]*h[i];FFt(a,-1);
    for (int i=m-1;i<n;i++)
        if ((int)(a[i].r+.5)==0)
            f[++s]=i-m+1;printf("%d\n",s);
    for (int i=1;i<=s;i++)
        printf("%d",f[i]+1),putchar(i<s?' ':'\n');
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值