CF 954I - Yet Another String Matching Problem FFT 字符串

题意:

给你两个字符串S,T。

对于每个T长度的S子串,你需要求出他们的“距离”。

你每次可以将一个字符变成另一个字符,对于两个串都变,距离就是变化次数最小值使得两串相同。

字符集a - f。

题解:

如果长度相同,和CF另一题一模一样,只要把同一位置的两个字符连边,然后并查集一下就可以了。

现在还是一样,只是多了FFT,我们枚举转换字符i, j, 6 * 6 = 36种,然后把S中所有i字符变成1,其他的为0, T中所有j字符变成1,其他的为0。然后跑FFT,res[i]>0代表有这种边。

代码:

#include <bits/stdc++.h>
#ifdef LOCAL
#define debug(x) cout<<#x<<" = "<<(x)<<endl;
#else
#define debug(x) 1;
#endif

#define chmax(x,y) x=max(x,y)
#define chmin(x,y) x=min(x,y)
#define lson id<<1,l,mid
#define rson id<<1|1,mid+1,r
#define lowbit(x) x&-x
#define mp make_pair
#define pb push_back
#define fir first
#define sec second
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;

const int MOD = 1e9 + 7;
const double PI = acos (-1.);
const double eps = 1e-10;
const int INF = 0x3f3f3f3f;
const ll INFLL = 0x3f3f3f3f3f3f3f3f;
const int MAXN = 3e5 + 5;

char s[MAXN], t[MAXN];

int par[MAXN][7];

int Find (int x, int i) {
    return x == par[i][x] ? x : par[i][x] = Find (par[i][x], i);
}

int Union (int x, int y, int i) {
    int xx = Find (x, i), yy = Find (y, i);
    if (xx != yy) {
        par[i][xx] = yy;
        return 1;
    }
    return 0;
}


//typedef complex<double> CP;
struct CP {
    double x, y;
    CP() {}
    CP (double x, double y) : x (x), y (y) {}
    inline double real() {
        return x;
    }
    inline CP operator * (const CP& r) const {
        return CP (x * r.x - y * r.y, x * r.y + y * r.x);
    }
    inline CP operator - (const CP& r) const {
        return CP (x - r.x, y - r.y);
    }
    inline CP operator + (const CP& r) const {
        return CP (x + r.x, y + r.y);
    }
    inline CP conj (const CP &r) {
        return CP (r.x, -r.y);
    }
};
CP a[MAXN], b[MAXN];
int r[MAXN], res[MAXN];
void fft_init (int nm, int k) {
// Rader 操作
    for (int i = 0; i < nm; ++i) r[i] = (r[i >> 1] >> 1) | ( (i & 1) << (k - 1) );
}
void fft (CP ax[], int nm, int op) {
    for (int i = 0; i < nm; ++i) if (i < r[i]) swap (ax[i], ax[r[i]]);
    for (int h = 2, m = 1; h <= nm; h <<= 1, m <<= 1) { // 枚举长度
        CP wn = CP (cos (op * 2 * PI / h), sin (op * 2 * PI / h) );
        for (int i = 0; i < nm; i += h) { // 枚举所有长度为 h 的区间
            CP w (1, 0); // 旋转因子
            for (int j = i; j < i + m; ++j, w = w * wn) { // 枚举角度
                CP t = w * ax[j + m]; // 蝴蝶操作
                ax[j + m] = ax[j] - t;
                ax[j] = ax[j] + t;
            }
        }
    }
    if (op == -1) for (int i = 0; i < nm; ++i) ax[i].x /= nm;
}
void trans (int ax[], int bx[], int n, int m) {
    int nm = 1, k = 0;
    while (nm < 2 * n || nm < 2 * m) nm <<= 1, ++k;
    for (int i = 0; i < n; ++i) a[i] = CP (ax[i], 0);
    for (int i = 0; i < m; ++i) b[i] = CP (bx[i], 0);
    for (int i = n; i < nm; ++i) a[i] = CP (0, 0);
    for (int i = m; i < nm; ++i) b[i] = CP (0, 0);
    fft_init (nm, k);
    fft (a, nm, 1);
    fft (b, nm, 1);
    for (int i = 0; i < nm; ++i) a[i] = a[i] * b[i];
    fft (a, nm, -1);
    nm = n + m - 1;
    for (int i = 0; i < nm; ++i) res[i] = (int) (a[i].real() + 0.5);
}

int ax[MAXN], bx[MAXN];
int ans[MAXN];

int main() {
#ifdef LOCAL
    freopen ("input.txt", "r", stdin);
#endif
    scanf ("%s %s", s + 1, t + 1);
    int n = strlen (s + 1), m = strlen (t + 1);
    reverse (t + 1, t + 1 + m);
    for (int i = 1; i <= n; i++) for (int j = 0; j < 7; j++) par[i][j] = j;
    for (int i = 0; i < 6; i++) {
        for (int j = 0; j < 6; j++) {
            for (int k = 1; k <= n; k++) ax[k] = s[k] == i + 'a';
            for (int k = 1; k <= m; k++) bx[k] = t[k] == j + 'a';
            trans (ax, bx, n + 1, m + 1);
            for (int k = m + 1; k <= n + 1; k++) if (res[k]) ans[k - m] += Union (i, j, k - m);
        }
    }
    for (int i = 1; i <= n - m + 1; i++) printf ("%d ", ans[i]);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/c6376315qqso/article/details/82982172