pat-至多刪三個字符(簡單DP)

題目鏈接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;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章