L3-2 至多删三个字符(30 分)
给定一个全部由小写英文字母组成的字符串,允许你至多删掉其中 3 个字符,结果可能有多少种不同的字符串?
输入格式:
输入在一行中给出全部由小写英文字母组成的、长度在区间 [4, 10
6
] 内的字符串。
输出格式:
在一行中输出至多删掉其中 3 个字符后不同字符串的个数。
输入样例:
ababcc
输出样例:
![](/qrcode.jpg)
25
提示:
删掉 0 个字符得到 "ababcc"。
删掉 1 个字符得到 "babcc", "aabcc", "abbcc", "abacc" 和 "ababc"。
删掉 2 个字符得到 "abcc", "bbcc", "bacc", "babc", "aacc", "aabc", "abbc", "abac" 和 "abab"。
删掉 3 个字符得到 "abc", "bcc", "acc", "bbc", "bac", "bab", "aac", "aab", "abb" 和 "aba"。
dp[i][j](i代表字符串长度,j代表删除的位数)
可推的dp[i][j]=dp[i-1][j-1]+dp[i-1][j]
因为增加的这一位要么被删除了,要么还保留着
现在得到的是一般字符串的递推公式,比如abcde
还有比较特殊的要处理,比如abab,aaaaa等
那么需要一些特判条件来满足
如果出现了XX这样相邻的:
1.那么dp[i][1]=dp[i-1][1](删一位的数量没变)
2.那么删两位的数量要比正常的减少dp[i-2][1];因为删第一个X和删第二 个X重复,所以要减去一个dp[i-2][1](前面i-2个中有一个被删)
3.那么删三位的数量要比正常的减少dp[i-2][2];因为删第一个X和删第二 个X重复,所以要减去一个dp[i-2][2](前面i-2个中有两个被删)
如果出现了X_X这样相邻的:(注意,不包括XXX这种情况)
1.删一位不用处理,与一般相同
2.删两位只会在删X_和删_X时相同,所以减1即可
3.删三位其实就是删_X_和删_ _X时重复,所以减去dp[i-3][1]即可
如果出现了X_X这样相邻的:(注意,不包括XXXX,XX_X,X_XX这几种情况)
1.删一位不用处理,与一般的相同
2.删两位不用处理,与一般的相同
3.删三位其实就是删X_ _和删_ _X时重复,所以减去1即可
#include<iostream> using namespace std; long long dp[1000005][4]; int main(){ string ss; cin>>ss; dp[1][0]=dp[1][1]=1; dp[0][2]=dp[0][3]=0; int n=int(ss.size()); for(int i=2;i<=n;i++){ dp[i][0]=1; for(int j=1;j<=3;j++){ dp[i][j]=dp[i-1][j-1]+dp[i-1][j]; if(ss[i-1]==ss[i-2]&&j==1) dp[i][j]-=1; else if(ss[i-1]==ss[i-2]&&j==2) dp[i][j]-=dp[i-2][1]; else if(ss[i-1]==ss[i-2]&&j==3) dp[i][j]-=dp[i-2][2]; if(i>=3&&ss[i-1]==ss[i-3]&&ss[i-1]!=ss[i-2]&&j==2) dp[i][j]-=1; else if(i>=3&&ss[i-1]==ss[i-3]&&ss[i-1]!=ss[i-2]&&j==3) dp[i][j]-=dp[i-3][1]; if(i>=4&&ss[i-1]==ss[i-4]&&ss[i-1]!=ss[i-2]&&ss[i-1]!=ss[i-3]&&j==3) dp[i][j]-=1; //printf("%lld ",dp[i][j]); } //printf("\n"); } cout<<dp[n][0]+dp[n][1]+dp[n][2]+dp[n][3]<<endl; return 0; }