https://pintia.cn/problem-sets/994805046380707840/problems/994805046946938880
大意:
给出一个字符串,每次最多可以删除3个字符,也可以不删字符,问删除字符后有多少个不同的字符串。
思路:
- 找状态: dp[i][j]表示前i个数中取走j个的不同字符串个数
- 状态转移:dp[i][j] = dp[i-1][j](不取走第i个字符)+dp[i-1][j-1](取走第i个字符),但这样会有重复情况,比如:abcdace,假设当前是取3个字符的情况,那么取cda和取dac,最后形成的字符串都是abe,所以往前找到第一个和当前字符相等的字符,进行去重。dp[i][j] -= dp[i][j-(i-k)].
- 赋初值:dp[0][0] = 1.
解释为什么是 dp[i][j] -= dp[i][j-(i-k)]:
比如:abcdce,假设第一个c下标为k,第二个字符下标为i,取3个字符,取[k,i-1]和[k+1,i]是一种情况。
而取cd已经取了两个字符了,再到[1,k-1]中取一个字符。所以是减去dp[i][j-(i-k)]。
code
#include <bits/stdc++.h>
#define ll long long
const int N = 1e6+7;
const int mod = 1e9+7;
const double eps = 1e-8;
using namespace std;
ll dp[N][5];//dp[i][j]表示前i个数中取走j个的不同字符串个数
char s[N];
void solve(){
cin >> s+1;
int n = strlen(s+1);
dp[0][0] = 1;
for(int i = 1; i <= n; i++){
for(int j = 0; j <= 3; j++){
dp[i][j] = dp[i-1][j];
if(j) dp[i][j] += dp[i-1][j-1];
for(int k = i-1; k >= 1 && j-(i-k) >= 0; k--){
if(s[k] == s[i]){
dp[i][j] -= dp[k-1][j-(i-k)];
break;
}
}
}
}
ll ans = 0;
for(int i = 0; i <= 3; i++){
ans += dp[n][i];
}
cout << ans << endl;
}
int main(){
// int t;
// cin >> t;
// while(t--)
solve();
//system("pause");
return 0;
}