CF954I. Yet Another String Matching Problem 字符串 快速傅里叶变换 FFT

版权声明:_ https://blog.csdn.net/lunch__/article/details/81949273

题目链接

这个题目这个算法应该是卡过去的, B y m 666 帮我改的时候还嘲讽我写 W A 了..
其实只是常数写大了点…

首先不考虑数据范围,考虑巧妙一点的暴力,每次枚举两个串匹配,对应的字符在并查集中连边,最后的答案就是并查集中的边数,但是枚举串匹配的复杂度是 O ( | S | | T | )

我们可以考虑用 F F T 优化匹配,由于字符集很小暴力枚举匹配是 O ( 30 )

对于每种子串匹配情况我们可以开一个并查集,每次把匹配到的字符设为 1 ,其他的设为 0 ,做一遍 F F T 来检查匹配,最后复杂度就是 O ( 30 n l o g n ) ,差不多刚刚可以卡过去,注意复数类手写,一开始偷懒就卡 T L E

#include<bits/stdc++.h>

#define num(x) (x - 'a' + 1)

using namespace std;
typedef long double db;

const int N = 5e5 + 10;
const db Pi = acos(-1);

struct Complex {
    db x, y;
    Complex(db xx = 0, db yy = 0) {
        x = xx, y = yy;
    }
    Complex operator + (const Complex &T) {
        return Complex(x + T.x, y + T.y);
    }
    Complex operator - (const Complex &T) {
        return Complex(x - T.x, y - T.y);
    }
    Complex operator * (const Complex &T) {
        return Complex(x * T.x - y * T.y, x * T.y + y * T.x);
    }
};

char S[N], T[N];
int n, m, rev[N], ans[N];
Complex a[N], b[N];

struct Union_Set {
    int fa[7];

    void Clear() {
        for(int i = 1; i <= 6; ++ i)
            fa[i] = i;
    }

    int Find(int x) {
        return fa[x] = x == fa[x] ? x : Find(fa[x]);
    }

    bool Merge(int x, int y) {
        x = Find(x), y = Find(y);
        if(x == y) return false;
        fa[x] = y; return true;
    }
}Tr[N];

void FFT(int n, Complex *a, int fh) {
    for(int i = 0; i < n; ++ i) if(i < rev[i]) swap(a[i], a[rev[i]]);
    for(int limit = 2; limit <= n; limit <<= 1) {
        db theta = 2.0 * Pi / limit;
        Complex Wn = Complex{cos(theta), sin(theta) * fh}, W = Complex{1, 0};
        for(int j = 0; j < n; j += limit, W = Complex{1, 0})
            for(int i = j; i < j + (limit >> 1); ++ i, W = W * Wn) {
                Complex a1 = a[i], a2 = a[i + (limit >> 1)] * W;
                a[i] = a1 + a2, a[i + (limit >> 1)] = a1 - a2;
            }
    }
    if(fh == -1) 
        for(int i = 0; i < n; ++ i)
            a[i].x = a[i].x / n;
}

int main() {
#ifndef ONLINE_JUDGE
    freopen("CF954I.in", "r", stdin);
    freopen("CF954I.out", "w", stdout);
#endif
    int limit = 1, k = 0;
    scanf("%s%s", S, T);
    n = strlen(S), m = strlen(T);

    while(limit <= (n + m - 2)) limit <<= 1, ++ k;

    for(int i = 0; i < limit; ++ i) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (k - 1));

    for(int i = 0; i <= n - m; ++ i) Tr[i].Clear();

    for(int Char1 = 1; Char1 <= 6; ++ Char1) 
        for(int Char2 = 1; Char2 <= 6; ++ Char2) {
            if(Char1 == Char2) continue;
        //  printf("%c %c\n", Char1 + 'a' - 1, Char2 + 'a' - 1);
            for(int i = 0; i < n; ++ i) a[i] = num(S[i]) == Char1;
            for(int i = 0; i < m; ++ i) b[i] = num(T[i]) == Char2;
        //  for(int i = 0; i < n; ++ i) printf("%d ", int(a[i].real())); puts("");
            reverse(b, b + m);
        //  for(int i = 0; i < m; ++ i) printf("%d ", int(b[i].real())); puts("");
            FFT(limit, a, 1), FFT(limit, b, 1);
            for(int i = 0; i < limit; ++ i) a[i] = a[i] * b[i];
            FFT(limit, a, -1);
        //  for(int i = 0; i < limit; ++ i) printf("%d ", int(a[i].real())); puts("");
            for(int i = 0; i <= n - m; ++ i) 
                if(a[m - 1 + i].x >= 0.5)
                    ans[i] += Tr[i].Merge(Char1, Char2);

            for(int i = 0; i < limit; ++ i) a[i] = b[i] = 0;
        }
    for(int i = 0; i <= n - m; ++ i) printf("%d ", ans[i]);
    //cerr << 1.0 * clock() / CLOCKS_PER_SEC << endl;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/lunch__/article/details/81949273