題目鏈接:https://pintia.cn/problem-sets/994805046380707840/problems/994805046946938880
題意:給定一個全部由小寫英文字母組成的字符串,允許你至多刪掉其中 3 個字符,結果可能有多少種不同的字符串?
思路::DP問題:找是否有重疊問題,明確遞推關係,怎麼推的(順序千萬不要搞錯),找到狀態方程,循環時注意邊界條件和方程式是否嚴格成立。
重疊問題:刪除至多 3 個字符
遞推關係:dp[i][j] 表示前i個字符刪掉j個字符結果有dp[i][j]種。
d[i][j+1]+=d[i-1][j](刪除第i個字符)
d[i][j]+=d[i-1][j](不刪除第i個字符)
如果只是這樣轉移肯定會有重複的。例如一個字符串cdabnaxy,你刪除abn和刪除bna後得到的字符串都是cdaxy。
這時候就要去重了,根據上面那個栗子可以發現對於一個字符s[i],如果在i之前存在一個x使得s[x]=s[i],那麼刪除[x,i-1]間的字符 和刪除[x+1,i]間的字符其實是重複的,可以自己模擬一下過程。
那麼d[i][j]就要減去這個重複,這個重複可表示爲:d[x-1][j-(i-x)]
d[x-1][j-(i-x)]如何得來的,可以看下圖進行理解:
代碼:
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+5;
long long dp[maxn][4];
char s[maxn];
int main(){
scanf("%s",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++){
if(dp[i-1][j]==0) continue;
if(j<3) dp[i][j+1]+=dp[i-1][j];//dp[i][j+1]這裏的j<3
dp[i][j]+=dp[i-1][j];
for(int k=i-1;k>=1&&i-k<=j;k--){//往前找到第一個k使得s[k]=s[i]
if(s[k]==s[i]){
dp[i][j]-=dp[k-1][j-(i-k)];
break;
}
}
}
}
printf("%lld\n",dp[n][0]+dp[n][1]+dp[n][2]+dp[n][3]);
return 0;
}