PTA团体程序设计天梯赛-练习集 L3-020 至多删三个字符 (dp)

原题链接
思路:
d p [ i ] [ j ] dp[i][j] dp[i][j]表示枚举到第 i i i位时,删除了 j j j个得到的不同的字符串个数。
转移的时候考虑第 i i i位是否被删除:
d p [ i ] [ j ] = d p [ i − 1 ] [ j ] + d p [ i − 1 ] [ j − 1 ] dp[i][j]=dp[i-1][j]+dp[i-1][j-1] dp[i][j]=dp[i1][j]+dp[i1][j1]
这样没有去掉重复的情况,什么情况才会导致重复计数呢。
比如 a b a b c c ababcc ababcc,枚举到第4位的时候,删除两个字符 a b ab ab,得到字符串位 a b c c abcc abcc,但是到第3位的时候删除两个字符也会得到这个字符串,这就导致了重复。
从当前枚举的位置 i i i往前看,因为后面的字符还没有开始删除,不会造成影响。
如果 s [ i ] = = s [ k ] s[i]==s[k] s[i]==s[k]说明删除 [ k , i − 1 ] [k,i-1] [k,i1] [ k + 1 , i ] [k+1,i] [k+1,i]得到的字符串是相同的,将前面的贡献减掉即可。
找到一个退出即可,因为是从左往右枚举的,前面不合法的已经去掉了。
代码:

#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
const int maxn=1e6+7;
ll res,dp[maxn][4];
char s[maxn];
int main(){
	cin>>s+1;
	int n=strlen(s+1);
	dp[0][0]=1;
	///dp[i][j]表示到第i位时 删除了j位 得到的字符串个数
	for(int i=1;i<=n;i++){
		for(int j=0;j<=3;j++){
			dp[i][j]=(dp[i][j]+dp[i-1][j]);
			if(j>=1) dp[i][j]=(dp[i][j]+dp[i-1][j-1]);
			for(int k=i-1;k>=i-j;k--)
				if(s[i]==s[k]){
					dp[i][j]-=dp[k-1][j-(i-k)];
					break;
				}
		}		
	}
	ll res=0;
	for(int i=0;i<4;i++)
		res+=dp[n][i];
	cout<<res<<endl;
	return 0;
}
posted @ 2021-04-20 08:15  OvO1  阅读(67)  评论(0编辑  收藏  举报