PTAL3-020 至多删三个字符(0/1背包)

PTAL3-020 至多删三个字符(0/1背包)

Description
给定一个全部由小写英文字母组成的字符串,允许你至多删掉其中 3 个字符,结果可能有多少种不同的字符串?
Input
输入在一行中给出全部由小写英文字母组成的、长度在区间[4, 10​6​​]内的字符串。
Output
在一行中输出至多删掉其中 3 个字符后不同字符串的个数。
Sample Input
ababcc
Sample Output
25

题意

要求删掉一串字符串中最多3个字符,结果最多可能有几种字符串。状态转移方程dp[i][j]+=(dp[i-1][j-1]+dp[i-1][j]);dp[i][j]表示前i个字符中删去了j个字符。dp[i-1][j-1]表示前i-1个字符中删去了j-1个字符。dp[i-1][j]表示前i-1个字符中删去了j个字符。如果选择把第i个字符删掉,所以剩下的不同字符的个数和前i-1个字符删去j-1个字符相等,所以dp[i-1][j-1]代表删去了第i个字符。如果选择留下第i个字符,则前面i-1个字符必须删掉j个字符,因此dp[i-1][j]表示留下了第i个字符。但是这样子还是有重复的情况,需要去重。我们从第i-1个字符往前找,找到i-k大于要删去的字符个数j为止,看这一段字符中是否有字符k与第i个字符是相同的。若有,则需要删去除了k到i中的重复的一段外,1到k-1这一段中与k到i这一段组合导致出现重复的几个字符(太难描述了),这些字符的个数即是j-(i-k)。下面附图。
在这里插入图片描述

#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<queue>
#include<stack>
#include<vector>
#include<algorithm>
#include<functional> 
#include<map>
#include<unordered_map>
#define lowbit(x) ((x)&-(x));
using namespace std;
typedef long long ll;
const int N=1e6+10,NN=2e3+10,INF=0x3f3f3f3f;
const ll MOD=1e9+7;
int len;
ll dp[N][4];//到第i个字符,删去j个后有几个不同的字符串 
char str[N];
void init(){
}
int main(){
	scanf("%s",str+1);
	len=strlen(str+1);
	dp[0][0]=1;
	for(int i=1;i<=len;i++){//枚举区间[1,i] 
		for(int j=0;j<=3;j++){//枚举删除个数
			if(j==0){//删去0个时,只有一个不同的字符串
				dp[i][j]=1LL;
				continue;
			}
			dp[i][j]+=(dp[i-1][j-1]+dp[i-1][j]);//判断第i个是否删去,dp[i-1][j-1]表示删去,dp[i-1][j]表示不删去
			for(int k=i-1;k>=1&&(i-k)<=j;k--){
				if(str[k]==str[i]){
					dp[i][j]-=dp[k-1][j-(i-k)];
					break;
				}
			}
		} 
	}
	printf("%lld\n",dp[len][0]+dp[len][1]+dp[len][2]+dp[len][3]);
}

猜你喜欢

转载自blog.csdn.net/Hc_Soap/article/details/107546458