BZOJ 4259: 残缺的字符串(构造+FFT)

Description

很久很久以前,在你刚刚学习字符串匹配的时候,有两个仅包含小写字母的字符串A和B,其中A串长度为m,B串长度为n。可当你现在再次碰到这两个串时,这两个串已经老化了,每个串都有不同程度的残缺。
你想对这两个串重新进行匹配,其中A为模板串,那么现在问题来了,请回答,对于B的每一个位置i,从这个位置开始连续m个字符形成的子串是否可能与A串完全匹配?


Input

第一行包含两个正整数m,n(1<=m<=n<=300000),分别表示A串和B串的长度。
第二行为一个长度为m的字符串A。
第三行为一个长度为n的字符串B。
两个串均仅由小写字母和*号组成,其中*号表示相应位置已经残缺。


Output

第一行包含一个整数k,表示B串中可以完全匹配A串的位置个数。
若k>0,则第二行输出k个正整数,从小到大依次输出每个可以匹配的开头位置(下标从1开始)。


Sample Input

3 7
a*b
aebr*ob


Sample Output

2
1 5


Solution

借了一个号,可以做权限题了。在讲题解之前,我必须要控诉,表达我一个下午挣扎的痛苦。这TM是一道正解为FFT的FFT卡常题!!

一个下午的TLE,先是写NTT,然后发现好像有点问题,但对拍没错,T。改成FFT,还是T(多半是废了)。

在和tututu的并肩奋战下,我灵机一现,加了register和输出优化等底层优化。终于卡过了QAQ。

这是一道FFT的套路题,对我而言又发现了一种新套路。长话短说就是把通配符设成0,然后字符串换成整数,构造函数F[i],我们神奇地发现,将短串反过来做一个卷积就是答案:

F[i]=j=0|S|1f[ij]g[j](f[ij]g[j])2=f[ij]3g[j]2f[ij]2g[j]2+f[ij]g[j]3 F [ i ] = ∑ j = 0 | S | − 1 f [ i − j ] ∗ g [ j ] ∗ ( f [ i − j ] − g [ j ] ) 2 = f [ i − j ] 3 ∗ g [ j ] − 2 ∗ f [ i − j ] 2 ∗ g [ j ] 2 + f [ i − j ] ∗ g [ j ] 3

于是我们就把这三项卷一下,判断F[i]是否为0就行了,为0则代表以i为末尾可匹配。

神奇的FFT处理字符串。

最后将次数界改小了一点,跑了6000ms+。


Code

#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#define maxn 300010
using namespace std;
int n, LS, LT, L;
char S[maxn], T[maxn];
long long F[maxn<<2];
int f[maxn], g[maxn], rev[maxn<<2];
const double PI = acos(-1.0);
struct Complex{
    double real, image;
    Complex() {}
    Complex(double _real, double _image){
        real = _real;
        image = _image;
    }
    friend Complex operator + (Complex A, Complex B){
        return Complex(A.real + B.real, A.image + B.image);
    }
    friend Complex operator - (Complex A, Complex B){
        return Complex(A.real - B.real, A.image - B.image);
    }
    friend Complex operator * (Complex A, Complex B){
        return Complex(A.real * B.real - A.image * B.image, A.real * B.image + A.image * B.real);
    }
}a[maxn<<2], b[maxn<<2];
inline void Reverse(){
    for(register int i = 0; i < n; i++)
        rev[i] = (rev[i>>1]>>1) | ((i&1)<<(L-1));
}
inline void FFT(Complex *A, int DFT){
    for(register int i = 0; i < n; i++)  if(rev[i] > i)  swap(A[i], A[rev[i]]);
    for(register int s = 1; (1<<s) <= n; s++){
        int m = 1 << s;
        Complex wn = Complex(cos(DFT*2*PI/m), sin(DFT*2*PI/m));
        for(register int k = 0; k < n; k += m){
            Complex w = Complex(1, 0);
            for(register int j = 0; j < (m>>1); j++){
                Complex u = A[k+j], t = w * A[k+j+(m>>1)];
                A[k+j] = u + t;
                A[k+j+(m>>1)] = u - t;
                w = w * wn;
            }
        }
    }
    if(DFT == -1)  for(register int i = 0; i < n; i++)  A[i].real /= n;
}
void Work(int op){
    FFT(a, 1);
    FFT(b, 1);
    for(register int i = 0; i < n; i++)  a[i] = a[i] * b[i];
    FFT(a, -1);
    for(register int i = 0; i < n; i++)  F[i] += (long long)(a[i].real + .5) * op;
}
inline int GET(int x){
    int t = 1;
    while(t < x)  t <<= 1, L ++;
    return t;
}
int sum, ans[maxn];
void Print(int x){
    if(x > 9)  Print(x / 10);
    putchar(x % 10 + '0');
}
int main(){
    scanf("%d%d", &LS, &LT);
    scanf("%s%s", S, T);
    n = GET(LT+LS);
    Reverse();
    for(register int i = 0; i < LS; i++){
        if(S[LS-i-1] == '*')  f[i] = 0;
        else  f[i] = S[LS-i-1] - 'a' + 1;
    }
    for(register int i = 0; i < LT; i++){
        if(T[i] == '*')  g[i] = 0;
        else  g[i] = T[i] - 'a' + 1;
    }
    for(register int i = 0; i < n; i++)   a[i] = b[i] = Complex(0, 0);
    for(register int i = 0; i < LS; i++)  a[i] = Complex(f[i]*f[i]*f[i], 0);
    for(register int i = 0; i < LT; i++)  b[i] = Complex(g[i], 0);
    Work(1);
    for(register int i = 0; i < n; i++)   a[i] = b[i] = Complex(0, 0);
    for(register int i = 0; i < LS; i++)  a[i] = Complex(f[i]*f[i], 0);
    for(register int i = 0; i < LT; i++)  b[i] = Complex(g[i]*g[i], 0);
    Work(-2);
    for(register int i = 0; i < n; i++)   a[i] = b[i] = Complex(0, 0);
    for(register int i = 0; i < LS; i++)  a[i] = Complex(f[i], 0);
    for(register int i = 0; i < LT; i++)  b[i] = Complex(g[i]*g[i]*g[i], 0);
    Work(1);
    for(register int i = LS-1; i < LT; i++)
        if(!F[i]){
            sum ++;
            ans[sum] = i - LS + 2;
        }
    printf("%d\n", sum);
    for(register int i = 1; i <= sum; i++)
        printf("%d ", ans[i]);
    puts("");
    return 0;
}

这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值