题目大意

给你两个字符串,长度在级别,询问在两者的全部子序列中,长度相等,并且的数量有多少个?

Solution

考点:相同子序列数量

如果直接考虑,因为子序列并不知道第几个位置不同,所以这个并不好处理,但是我们一定可以把这个的子序列分成段。

第一段:子序列前个字符都相同。

第二段:

第三段:剩余长度任意挑选子序列。

先考虑如何求解任意两个位置相同子序列数量,这个用动态规划处理。

我们用代表以个字符,个字符的相同子序列数量,那么我们可以写出下面的转移方程。

的时候减掉的是重复计算的,当的时候加上的是一定选他们结尾的时候前面相同的,加上前面一个不选。

好了我们已经预处理出了数组了,下面就要考虑后面长度任选怎么处理。

如果我们枚举到串后面还剩长度为串后面长度为,那么等价与求

上面这个式子可以做个等价变换,首先我们知道,那么就变成了我有两堆球,我要在左边拿出个球,右边拿出个球的方案数总和,并且提供这个乘法之后求合我们可以判断这些球都是完全不一样的,那不就等价于我一共有个球,要拿出个球的方案数吗,所以我们得到

接下来就只要计算答案就行了,总的时间复杂度是

const int N = 5e3 + 7;
ll n, m;
int f[N][N];

ll jc[N << 1], inv[N << 1];
void init() {
    jc[0] = 1;
    for (int i = 1; i < N * 2; ++i)    jc[i] = jc[i - 1] * i % MOD;
    inv[N * 2 - 1] = qpow(jc[N * 2 - 1], MOD - 2, MOD);
    for (int i = N * 2 - 2; ~i; --i)    inv[i] = inv[i + 1] * (i + 1) % MOD;
}
ll C(int n, int m) {
    return jc[n] * inv[m] % MOD * inv[n - m] % MOD;
}

char s[N], t[N];

int solve() {
    scanf("%s", s + 1);
    scanf("%s", t + 1);
    n = strlen(s + 1), m = strlen(t + 1);
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= m; ++j) {
            if (s[i] == t[j]) {
                f[i][j] = (f[i - 1][j] + f[i][j - 1] + 1) % MOD;
            }
            else {
                f[i][j] = ((f[i - 1][j] + f[i][j - 1]) % MOD - f[i - 1][j - 1] + MOD) % MOD;
            }
        }
    }
    int res = 0;
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= m; ++j) {
            if (s[i] < t[j]) {
                res = (res + (f[i - 1][j - 1] + 1) * C(n - i + m - j, n - i) % MOD) % MOD;
            }
        }
    }
    print(res);

    return 1;
}