首页 > 代码库 > 省队集训 Day1 残缺的字符串

省队集训 Day1 残缺的字符串

【题目大意】

双串带通配符匹配。

$|S|, |T| \leq 5 * 10^5$

TL: 2s

【题解】

参考bzoj 4503

可以设计如下函数 A[i] * B[i] * (A[i] - B[i])^2

如果有通配符,A[i] = 0,否则,A[i] = s[i] - ‘a‘ + 1;B同理。

可以自行验证,这是一种很妙的设计。

然后就是卷积的事情了。大概做9次DFT。

可以用类似于MTT的技巧搞到4次,不会写。

技术分享
# include <math.h>
# include <stdio.h>
# include <string.h>
# include <iostream>
# include <algorithm>
// # include <bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
const int M = 5e5 + 10, N = 2e6 + 10;
const int mod = 1e9+7;
const ld pi = acos(-1.0);

char s[M], t[M];
int A[M], B[M], ns, nt;

struct cp {
    ld x, y;
    cp() {}
    cp(ld x, ld y) : x(x), y(y) {}
    friend cp operator + (cp a, cp b) {
        return cp(a.x + b.x, a.y + b.y);
    }
    friend cp operator - (cp a, cp b) {
        return cp(a.x - b.x, a.y - b.y);
    }
    friend cp operator * (cp a, cp b) {
        return cp(a.x * b.x - a.y * b.y, a.x * b.y + a.y * b.x);
    }
}a[N], b[N], ans[N];

namespace FFT {
    int n, lst[N]; cp w[2][N];
    inline void set(int _n) {
        n = 1;
        while(n < _n) n <<= 1;
        for (int i=0; i<n; ++i) w[0][i] = cp(cos(pi * 2 / n * i), sin(pi * 2 / n * i)), w[1][i] = cp(w[0][i].x, -w[0][i].y);
        int len = 0;
        while((1<<len) < n) ++len;
        for (int i=0; i<n; ++i) {
            int t = 0;
            for (int j=0; j<len; ++j) if(i & (1<<j)) t |= (1<<(len-j-1));
            lst[i] = t;
        }
    }
    inline void DFT(cp *a, int op) {
        cp *o = w[op];
        for (int i=0; i<n; ++i) if(i < lst[i]) swap(a[i], a[lst[i]]);
        for (int len=2; len<=n; len<<=1) {
            int m = len>>1;
            for (cp *p=a; p!=a+n; p+=len) {
                for (int k=0; k<m; ++k) {
                    cp t = o[n/len*k] * p[k+m];
                    p[k+m] = p[k] - t;
                    p[k] = p[k] + t;
                }
            }
        }
        if(op) {
            for (int i=0; i<n; ++i) a[i].x /= (ld)n, a[i].y /= (ld)n;
        }
    }
}

# define L FFT::n

int main() {
    scanf("%s%s", t, s); ns = strlen(s), nt = strlen(t);
    for (int i=0; i<ns; ++i) A[i] = (s[i] == * ? 0 : s[i] - a + 1);
    for (int i=0; i<nt; ++i) B[i] = (t[i] == * ? 0 : t[i] - a + 1);
    reverse(B, B+nt);
    // (A[i] - B[i])^2 * A[i] * B[i]  =  A[i]^3 * B[i] + A[i] * B[i]^3 - A[i]^2 * B[i]^2
    FFT :: set(max(ns, nt));
    for (int i=0; i<ns; ++i) a[i] = cp(A[i] * A[i] * A[i], 0);
    for (int i=ns; i<L; ++i) a[i] = cp(0, 0);
    for (int i=0; i<nt; ++i) b[i] = cp(B[i], 0);
    for (int i=nt; i<L; ++i) b[i] = cp(0, 0);
    FFT :: DFT(a, 0); FFT :: DFT(b, 0);
    for (int i=0; i<L; ++i) ans[i] = ans[i] + a[i] * b[i];
    for (int i=0; i<ns; ++i) a[i] = cp(A[i], 0);
    for (int i=ns; i<L; ++i) a[i] = cp(0, 0);
    for (int i=0; i<nt; ++i) b[i] = cp(B[i] * B[i] * B[i], 0);
    for (int i=nt; i<L; ++i) b[i] = cp(0, 0);
    FFT :: DFT(a, 0); FFT :: DFT(b, 0);
    for (int i=0; i<L; ++i) ans[i] = ans[i] + a[i] * b[i];
    for (int i=0; i<ns; ++i) a[i] = cp(A[i] * A[i] * 2, 0);
    for (int i=ns; i<L; ++i) a[i] = cp(0, 0);
    for (int i=0; i<nt; ++i) b[i] = cp(B[i] * B[i], 0);
    for (int i=nt; i<L; ++i) b[i] = cp(0, 0);
    FFT :: DFT(a, 0); FFT :: DFT(b, 0);
    for (int i=0; i<L; ++i) ans[i] = ans[i] - a[i] * b[i];
    FFT :: DFT(ans, 1);
    for (int i=nt-1; i<ns; ++i) {
        if((int)(ans[i].x-0.5) == 0) printf("%d ", i-nt+2);
    }
    puts("");
    return 0;
}
View Code

可能是noi前最后一次复习FFT了。

省队集训 Day1 残缺的字符串