题意:
给定一个全部由小写英文字母组成的字符串,允许你至多删掉其中 3 个字符,结果可能有多少种不同的字符串?
题解:
最不擅长dp
我们设dp[i][j]表示前i里面删除j个有多少种方法
第i个删除或者不删除有两种情况
dp[i][j] = dp[i-1][j] + dp[i-1][j-1]
但是会存在重复的情况,我们还需要去重,什么情况会出现重复?
很明显…X…X这种情况,(…代表中间的几个字符,X代表相同字符)会重复,因为如果把省略号部分删去,再删除前X或者X(只保留一个X),答案就会重复。
所以要去重的情况有,出现两个相同的字符,两个相同字符的距离(坐标差的绝对值)不能超过3,
if(s[k]==s[i])
dp[i][j] - = dp[k-1][j-(i-k)];
代码:
#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;
}