CF528D Fuzzy Search FFT

中文题面
题目中有个k的限制比较麻烦,我们稍微转换一下,令(g[i][j])表示第(i)位,第(j)个字母,能否在k的范围内匹配上。
那么这个数组可以由类似滑动窗口的方式来解决:
新定义一个数组(T[i][j])表示S串中第(i)个字母是不是(j),如果是则为1,反之为0.(注意与原题面中的T串相区分)
那么(g[i][j])为1当且仅当向左向右分别扩展最多k位的空间内,(s)数组中有值为1,即

[g[i][j] = max(sum_{l = i - k}^{l <= i + k}T[l][j]) ]

那么k的限制就被消除了,因此下文中的k仅仅作为循环变量出现,与原题意中的k没有任何关系
下文写的是思路过程,在一些下标问题上并不一定严谨,具体的边界,下标之类的还要看具体实现(代码)

(F[i])表示以第i位为开头进行匹配,能够匹配上的最大长度。
那么有:

[F[i] = sum_{j = i}^{j le i + |T| - 1}sum_{k = 1}^{4} g[j][k] cdot T[j - i][k] ]

[F[i] = sum_{k = 1}^{4} sum_{j = i}^{j le i + |T| - 1}g[j][k] cdot T[j - i][k] ]

因此如果我们先枚举k,对于每个k单独计算后面部分并求和,那么如果(F[i] == |T|)则++ans;
因为先枚举了k,因此(g[j][k])(T[j][k])后面的参数k就暂时略去(方便书写)
观察到(g[j][k] cdot T[j - i][k])类似与卷积形式,因此我们将(T)数组翻转得到原式的新的表达式:

[f[i] = sum_{j = i}^{j le i + |T| - 1} g[j] cdot T[|T| - j + i] ]

因为

[j + |T| - j + i = |T| + i ]

所以

[f[i] = (g * T)(|T| + i) ]

直接上FFT / NTT即可

#include<bits/stdc++.h>
using namespace std;
#define R register int
#define AC 410000
#define ac 850000
#define LL long long
#define p 998244353

const int G = 3, Gi = 332748118;
int n, m, k, lim = 1, len, head, tail, inv, ans;
int g[ac], a[ac], f[AC], rev[ac], s[ac], q[ac];
char S[AC], T[AC], L[10] = {'A', 'G', 'C', 'T'};

inline void upmax(int &a, int b){if(b > a) a = b;}

inline int qpow(int x, int have)
{
    int rnt = 1;
    while(have)
    {
        if(have & 1) rnt = 1LL * rnt * x % p;
        x = 1LL * x * x % p, have >>= 1;
    }
    return rnt;
}

void pre()
{
    scanf("%d%d%d%s%s", &n, &m, &k, S, T);
    -- n, -- m;//让下标从0开始?
    while(lim <= n + m) lim <<= 1, len ++;
    for(R i = 0; i < lim; i ++) 
        rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (len - 1));	
    inv = qpow(lim, p - 2);//lim的inv
}

void build(int x)//滑动窗口预处理相应的数组
{
    for(R i = 0; i <= n; i ++) s[i + 1] = (S[i] == L[x]);//s数组从1开始处理比较方便
    memset(g, 0, sizeof(g)), memset(a, 0, sizeof(a));
    head = 1, tail = 0;
    int tmp = n + 1;
    for(R i = 1; i <= n + 1; i ++)
    {
        while(head <= tail && s[q[tail]] < s[i]) -- tail;//判断队内有无元素是要加<=的!!!
        q[++ tail] = i;
        while(head <= tail && q[head] < i - k) ++ head;
        upmax(g[i - 1], s[q[head]]);
    }
    head = 1, tail = 0;
    for(R i = tmp; i; i --)
    {
        while(head <= tail && s[q[tail]] < s[i]) -- tail;
        q[++ tail] = i;
        while(head <= tail && q[head] > i + k) ++ head;
        upmax(g[i - 1], s[q[head]]);
    }
    for(R i = 0; i <= m; i ++) a[i] = (T[i] == L[x]); 
    reverse(a, a + m + 1);//翻转
    /*printf("%c
", x + 'A'); 
    for(R i = 0; i <= n; i ++) printf("%d ", g[i]);
    printf("
");
    for(R i = 0; i <= m; i ++) printf("%d ", a[i]);
    printf("

");*/
}

void NTT(int *A, int opt)
{
    for(R i = 0; i < lim; i ++)
        if(i < rev[i]) swap(A[i], A[rev[i]]);
    for(R i = 1; i < lim; i <<= 1)
    {
        int W = qpow((opt == 1) ? G : Gi, (p - 1) / (i << 1));
        for(R r = i << 1, j = 0; j < lim; j += r)
            for(R w = 1, l = 0; l < i; l ++, w = 1LL * w * W % p)
            {
                int x = A[j + l], y = 1LL * w * A[j + i + l] % p;
                A[j + l] = (x + y) % p, A[j + i + l] = (x - y + p) % p;
            }
    }
}

void work()
{ 
    for(R i = 0; i <= 3; i ++)
    {
        build(i);
        /*printf("%d
", lim);
        for(R j = 0; j <= n; j ++) printf("%d ", g[j]);
        printf("
");
        for(R j = 0; j <= m; j ++) printf("%d ", a[j]);
        printf("
");*/
        NTT(a, 1), NTT(g, 1);
        for(R j = 0; j < lim; j ++) a[j] = 1LL * a[j] * g[j] % p;
        NTT(a, -1);
        for(R j = 0; j < lim; j ++) a[j] = 1LL * a[j] * inv % p;
        for(R j = 0; j < lim; j ++) f[j] += a[m + j];
    /*	for(R j = 0; j <= n - m; j ++) printf("%d ", a[m + 1 + j]);
        printf("

");*/
    }
    /*printf("!!!");
    for(R i = 1; i <= n - k + 1; i ++) printf("%d ", f[i]);
    printf("
");*/
    for(R i = 0; i <= n - m; i ++) ans += (f[i] == m + 1);//只有和为|T|才能计入答案
    printf("%d
", ans);
}


int main()
{
//	freopen("in.in", "r", stdin);
    pre();
    work();
//	fclose(stdin);
    return 0;
}
原文地址:https://www.cnblogs.com/ww3113306/p/10277726.html