pta l3-20(至多删三个字符)

时间:2023-03-08 21:58:23

题目链接:https://pintia.cn/problem-sets/994805046380707840/problems/994805046946938880

题意:给定一个长度<=106的字符串,求至多删3个字符可以得到多少种不同的字符串。

思路:很明显这是一道dp题,但我想了好久也想不出来,试过用dfs暴力去解,然后过了两个点,得了16分。看了别人的思路后明白了本题的dp做法。定义dp[maxn][4],dp[i][j]表示前i个字符删掉j个字符可以形成多少种字符串,可以很简单地得出dp[i][j]=dp[i-1][j-1]+dp[i-1][j],即对第i个字符删或不删的两种情况。这是对一般情况,其中还有一些可能出现重复的情况需要删掉:每一次将可能重复的情况全部删掉,那么每一次只用考虑第i个字符可能出现的重复的情况; 例如abcded,对于i=6时,计算dp[6][1]时不会有重复; 计算dp[6][2]时,若删掉ed,与删掉de出现重复,需要减掉删掉ed的情况,删掉ed,即前3个删0个,所以应减dp[3][0]; 计算dp[6][3]时,若删掉ed和abc中的一个,与删掉de和abc中的一个重复,需要减去前一种情况,即删掉前3个中的1个,应减dp[3][1]。

  那么不访用pos数组表示每个字符上一次出现的位置,记为tmp,若tmp>0(数组从下标1开始)且j-(i-tmp)>=0时:dp[i][j]-=dp[tmp-1][j-(i-tmp)]。(其中i-tmp即上例中的2,j-(i-tmp)即前tmp-1个数应删掉的字符。另外,最终结果可能超过int表示的范围,所以需用long long。

AC代码:

 #include<bits/stdc++.h>
using namespace std; const int maxn=;
typedef long long LL;
char s[maxn];
LL dp[maxn][];
int pos[]; int main(){
scanf("%s",s+);
int len=strlen(s+);
for(int i=;i<=len;++i)
dp[i][]=;
for(int i=;i<=len;++i){
int tmp=pos[s[i]-'a'];
pos[s[i]-'a']=i;
for(int j=;j<=;++j){
dp[i][j]=dp[i-][j-]+dp[i-][j];
if(j-(i-tmp)>=)
dp[i][j]-=dp[tmp-][j-(i-tmp)];
}
}
printf("%lld\n",dp[len][]+dp[len][]+dp[len][]+dp[len][]);
return ;
}