[COGS 2216]你猜是不是KMP

【题目描述】

XX在玩两个串的游戏。首先,他拿出了两个字符串 S 和 T,XX想知道 T
在 S 中出现了几次,分别在哪些位置出现。注意 T 中可能有“?”字符,这个字
符可以匹配任何字符。
【输入格式】

两行两个字符串,分别代表 S 和 T
【输出格式】

第一行一个正整数 k,表示 T 在 S 中出现了几次。
接下来 k 行正整数,
分别代表 T 每次在 S 中出现的开始位置。按照从小到大
的顺序输出,S 下标从 0 开始。
【样例输入】

ababcadaca
a?a
【样例输出】

3
0
5
7
【提示】

对于 10%的数据, S 和 T 的长度不超过 100
对于另外 20%的数据,T 中无“?”
对于 100%的数据,S 长度不超过 10^5,T 长度不会超过 S。S 中只包含小写
字母,T 中只包含小写字母和“?”
【来源】

经典题目

我猜是FFT 。。QAQ

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define maxn 300010
using namespace std;

struct cpx{
    double r, i;
    cpx(double a = 0, double b = 0):r(a), i(b){}
    cpx operator+(const cpx& o)const{return cpx(r+o.r, i+o.i);}
    cpx operator-(const cpx& o)const{return cpx(r-o.r, i-o.i);}
    cpx operator*(const cpx& o)const{return cpx(r*o.r-i*o.i, r*o.i+i*o.r);}
    cpx operator/(const double& k)const{return cpx(r/k, i/k);}
}P[maxn], A[maxn], B[maxn], C[maxn], D[maxn];
int r[maxn], l;
int rev(int S){
    int ret = 0;
    for(int i = 0; i < l; i ++){
        ret = ret << 1 | (S & 1);
        S >>= 1;
    }return ret;
}

const double pi = acos(-1.0);

void FFT(cpx A[], int n, int t){
    for(int i = 0; i < n; i ++) P[r[i]] = A[i];
    for(int i = 0; i < n; i ++) A[i] = P[i];
    for(int k = 2; k <= n; k <<= 1){
        cpx wn = cpx(cos(2*pi/k), t*sin(2*pi/k));
        for(int i = 0; i < n; i += k){
            cpx w = cpx(1, 0);
            for(int j = 0; j < k>>1; j ++){
                cpx T = w * A[i+j+(k>>1)];
                A[i+j+(k>>1)] = A[i+j] - T;
                A[i+j] = A[i+j] + T;
                w = w * wn;
            }
        }
    }
    if(t == -1)
        for(int i = 0; i < n; i ++)
            A[i] = A[i] / n;
}

char s[maxn];

int a[maxn], b[maxn], amt, ans[maxn];

const double eps = 1e-5;
int Filter(double x){return x < eps && x > -eps;}

long long sum;
double Q[maxn];

int main(){
    freopen("guess.in", "r", stdin);
    freopen("guess.out", "w", stdout);
    scanf("%s", s);
    int n1 = strlen(s);
    for(int i = 0; i < n1; i ++)
        a[i] = s[i] - 'a' + 1;
    scanf("%s", s);
    int n2 = strlen(s);
    for(int i = 0; i < n2; i ++){
        if(s[i] == '?') b[n2-i-1] = 0;
        else b[n2-i-1] = s[i] - 'a' + 1;
    }

    for(int i = 0; i < n2; i ++)
        sum += (long long)b[i] * b[i] * b[i];

    int m1 = n1 + n2, n = 1; l = 0;
    for(; n <= m1; n <<= 1, l ++);
    for(int i = 1; i < n; i ++)r[i] = rev(i);

    for(int i = 0; i < n1; i ++)A[i].r = (double)a[i] * a[i];
    for(int i = 0; i < n2; i ++)B[i].r = b[i];
    FFT(A, n, 1), FFT(B, n, 1);
    for(int i = 0; i < n; i ++) C[i] = A[i] * B[i];
    FFT(C, n, -1);

    memset(A, 0, sizeof A);
    memset(B, 0, sizeof B);
    for(int i = 0; i < n1; i ++)A[i].r = a[i];
    for(int i = 0; i < n2; i ++)B[i].r = (double)b[i] * b[i];
    FFT(A, n, 1), FFT(B, n, 1);
    for(int i = 0; i < n; i ++) D[i] = A[i] * B[i];
    FFT(D, n, -1);
    //for(int i = 0; i < n; i ++)printf("%.3lf ", C[i].r);puts("");

    for(int i = 0; i < n; i ++)
        Q[i] = C[i].r - 2 * D[i].r + sum;

    for(int i = 0; i+n2-1 < n1; i ++)
        if(Filter(Q[i+n2-1]))
            ans[++ amt] = i;
    printf("%d\n", amt);
    for(int i = 1; i <= amt; i ++)
        printf("%d\n", ans[i]);
    return 0;
}

/*
Input
ababcadaca
a?a
Output
3
0
5
7
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值