动态规划 至多删三个字符
题目链接
看题目,很明显的动态规划,可以分解成一个个相同的小问题。
先思考dp怎么设定,最多删除三个字符,所以对每个字符串有四种状态,没有删除,删除一个,两个,三个,所以二维的dp两个序号应该一个是表示字符的起点终点啥的,一个表示状态,这里dp[i][j]表示前i的字符,删除了j个字符的可能。
没有删除,那么dp[i][j]=dp[i-1][j],删除了当前那个dp[i][j]=dp[i-1][j-1]。需要注意一些i,j的可能性的判断,然后重点,错在这里想了好久,好不容易想到了又不会改,如果出现两个字符相同的情况,比如acdae,删除acd何cda是一样的,一样的,那原本的方法就会重复一倍,那该怎么去重的。
先看重复的部分是哪些,假设k是前一个相同的字符序号,那是不是0到k-1这一段被重复计算了(以上面那个例子为例,相同的部分是xxx acd ae和xxxa cda e,所以前面的xxx是重复的部分),然后两个相同字符中间,加上一个相同字符被删除,那就是删除了i-k个,原本应该删除j个字符,那还需要删除j-(i-k)个字符,故减去dp[k-1][j-(i-k)]
copy的网上大佬的代码,自己写的太复杂,难看懂,自卑
引用网址:https://blog.csdn.net/qq_37275680/article/details/88383950,侵删
#include<iostream>
#include<string>
#include<cstring>
#include<vector>
#include<set>
#include<cmath>
using namespace std;
typedef long long ll;
const int maxn=1e6+10;
char s[maxn];
ll dp[maxn][4];
int main(){
scanf("%s",s+1);
int len=strlen(s+1);
dp[0][0]=1; //讲真,这里赋值为1还是挺疑惑的
for(int i=1;i<=len;i++){
for(int j=0;j<=3;j++){
if(i<j) break; //前i个都不够删肯定不行呀
dp[i][j]=dp[i-1][j]; //第i位字符不删
if(j>=1) dp[i][j]+=dp[i-1][j-1]; //第i位字符删
for(int k=i-1;k>=1&&i-k<=j;k--){
if(s[k]==s[i]){
dp[i][j]-=dp[k-1][j-(i-k)];
break;
}
}
}
}
ll res=0;
for(int i=0;i<=3;i++){
res+=dp[len][i];
}
printf("%lld\n",res);
return 0;
}