跳转至

Design Tutorial: Increase the Constraints

题意

给定两个 01 串 ab,每次询问 ab 的长度相同的子串的汉明距离。

解析

考虑将 a 串分块,然后计算每一块与 b 的每个长度与之相同的子串的汉明距离。设每个块的大小为 S,则这样要占用 O\left(\dfrac{nm}{S}\right) 的空间。

如果暴力匹配 a[iS\ldots (i+1)S-1]b 的每个长度为 S 的子串,则总复杂度为 O(nm)。但由于字符集大小为 2,我们可以用 FFT 以 O\left(\dfrac{nm\log m}{S}\right) 的时间复杂度求出每一块与 b 的每一个相等长度的子串的汉明距离。

之后每次询问我们可以 O\left(\dfrac{n}{S}+S\right) 回答。取 S=\sqrt{n\log n} 得到理论最优复杂度 O(n\sqrt{n\log n})

实现

#include <bits/stdc++.h>

typedef long long int ll;
const int maxn = 3e5 + 19, mod = 1004535809, maxs = 200;

int qpow(int a, int b){
    int res = 1;
    while(b){
        if(b & 1) res = (ll)res * a % mod;
        a = (ll)a * a % mod, b >>= 1;
    }
    return res;
}

int rev[maxn];
void dft(int *f, int n, int b = 1){
    for(int i = 0; i < n; ++i)
        if(i < rev[i])
            std::swap(f[i], f[rev[i]]);
    static int w[maxn];
    for(int i = 2; i <= n; i <<= 1){
        w[0] = 1, w[1] = qpow(3, (mod - 1) / i);
        if(b == -1) w[1] = qpow(w[1], mod - 2);
        for(int j = 2; j < i / 2; ++j) w[j] = (ll)w[j - 1] * w[1] % mod;
        for(int j = 0; j < n; j += i){  
            int *g = f + j, *h = f + j + i / 2;
            for(int k = 0; k < i / 2; ++k){
                int p = g[k], q = (ll)h[k] * w[k] % mod;
                g[k] = (p + q) % mod, h[k] = (p - q) % mod;
            }
        }
    }
    if(b == -1)
        for(int i = 0, inv = qpow(n, mod - 2); i < n; ++i)
            f[i] = ((ll)f[i] * inv % mod + mod) % mod;
}

int n, m, S;
int belong[maxn], dif[maxs][maxn];
char a[maxn], b[maxn];
int f[maxn], g[maxn];

int main(){
    std::scanf("%s%s", a, b);
    n = std::strlen(a), m = std::strlen(b);
    S = std::max<int>(std::sqrt(std::max(n, m) * std::log(std::max(n, m))), n / (maxs - 2) + 1);

    for(int i = 0; i < n; ++i)
        belong[i] = i / S;
    for(int i = 0; i < m; ++i)
        f[i] = b[m - 1 - i] - '0';

    static int sumg, sumf[maxn];
    sumf[0] = b[0] - '0';
    for(int i = 1; i < m; ++i) sumf[i] = sumf[i - 1] + b[i] - '0';

    int N = 2;
    while(N < m + S - 1) N <<= 1;
    for(int i = 0; i < N; ++i) rev[i] = (rev[i >> 1] >> 1) | (i & 1 ? N >> 1 : 0);
    dft(f, N);

    for(int i = 0; i * S < n; ++i){
        int l = i * S, r = std::min(l + S, n) - 1, len = r - l + 1;
        sumg = 0;
        for(int j = 0; j < N; ++j) g[j] = 0;
        for(int j = 0; j < len; ++j)
            g[j] = a[l + j] - '0', sumg += g[j];
        dft(g, N);
        for(int j = 0; j < N; ++j) g[j] = (ll)g[j] * f[j] % mod;
        dft(g, N, -1);
        for(int j = 0; j + len - 1 < m; ++j)
            dif[i][j] = -2 * g[m - 1 - j] + sumf[j + len - 1] - sumf[j] + (b[j] - '0') + sumg;
    }

    std::scanf("%d", &m);
    while(m--){
        static int p1, p2, len, res;
        std::scanf("%d%d%d", &p1, &p2, &len), res = 0;
        while(p1 % S && len) res += a[p1++] != b[p2++], --len;
        while(len % S && len) res += a[p1 + len - 1] != b[p2 + len - 1], --len;
        int l = p1 / S, r = l + len / S - 1;
        for(int i = l; i <= r; ++i) res += dif[i][p2 + (i - l) * S];
        std::printf("%d\n", res);
    }
}

来源

Codeforces Round #270 G. Design Tutorial: Increase the Constraints

评论