L3-020 至多删三个字符 [DP]
这题在网上看到一个非常容易理解的思路,和大家分享一下。
记dp[i][j]为前i个字符删除j个字符后得到不同字符串的数量,可以得到以下两个转移方程
dp[i][j+1]=dp[i][j+1]+dp[i-1][j] (删除s[i])
dp[i][j]=dp[i][j]+dp[i-1][j] (不删除s[i])
如果只用上述式子,是会重复的。比如abcdecf,删除cde得到abcf,删除dec得到的也是abcf。
所以要删除重复计算的。从当前的i向左扫,扫到的第一个与s[i]相同的字符时处理,假设为s[k],那么dp[i][j]=dp[i][j]-dp[k-1][j-(i-k)]。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<iostream> #include<cstdio> #include<string.h> #include<math.h> #define maxn 1000005 using namespace std; typedef long long ll; char s[maxn]; ll dp[maxn][5]; int main() { scanf("%s",s+1); int l=strlen(s+1); dp[0][0]=1; for(int i=0;i<=l;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]+=dp[i-1][j]; 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;//如果有多个,因为是从前往后推的,所以在前面减过了 } } } } printf("%lld\n",dp[l][0]+dp[l][1]+dp[l][2]+dp[l][3]); return 0; }