L3-020 至多删三个字符 (30分) (DP)

问题描述:

给定一个全部由小写英文字母组成的字符串,允许你至多删掉其中 3 个字符,结果可能有多少种不同的字符串?

输入格式:

输入在一行中给出全部由小写英文字母组成的、长度在区间 [4, 1e6] 内的字符串。

输出格式:

在一行中输出至多删掉其中 3 个字符后不同字符串的个数。


解法:

d[i][j]表示前i个字符,删掉j个字符的方案数,
转移方程:d[i][j]=d[i-1][j]+d[i-1][j-1],(对应删和不删)
但是这样会重复计算,例如
caba,删除ab和删除ba得到的串是一样的,
记pre[s[i]]为s[i]上一次出现的位置,
设x=pre[s[i]]那么d[i][j]-=d[x-1][j-(i-x)],
为什么这样减?
因为重复匹配的部分是d[x-1][j-(i-x)],需要减掉一次.

ACcode

// Author : RioTian
// Time : 20/11/24
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6 + 10;
ll n, dp[N][4], pre[26];
string s;
int main() {
    // freopen("in.txt", "r", stdin);
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    cin >> s;
    s = "a" + s, n = s.length() - 1;
    dp[0][0] = 1;
    for (int i = 1; i <= n; ++i) {
        dp[i][0] = 1;
        int x = pre[s[i] - 'a'];
        for (int j = 1; j <= 3 && j <= i; ++j) {
            dp[i][j] = dp[i - 1][j] + dp[i - 1][j - 1];
            if (x && j - (i - x) >= 0)
                dp[i][j] -= dp[x - 1][j - (i - x)];
        }
        pre[s[i] - 'a'] = i;
    }
    ll ans = 0;
    for (int i = 0; i <= 3; ++i)
        ans += dp[n][i];
    cout << ans << endl;
}

The desire of his soul is the prophecy of his fate
你灵魂的欲望,是你命运的先知。

原文地址:https://www.cnblogs.com/RioTian/p/14032522.html