[CSP-S模拟测试]:抛硬币(DP)

题目背景

小$A$和小$B$是一对好朋友,他们经常一起愉快的玩耍。最近小$B$沉迷于**师手游,天天刷本,根本无心搞学习。
但是小$B$已经入坑了几个月,却一次都没有抽到$SSR$,让他非常怀疑人生。
勤勉的小$A$为了劝说小$B$早日脱坑,认真学习,决定以抛硬币的形式让小$B$明白他是一个彻彻底底的非洲人,从而对这个游戏绝望。


题目传送门(内部题43)


输入格式

一行一个字符串$S$,字符集为所有小写字母。第二行一个数$L$如题意。


输出格式

一行一个数,表示本质不同的长度为$L$的子序列个数,答案对$998244353$取模。


样例

样例输入:

addeade
3

样例输出:

19


数据范围与提示

我们定义$|S|$为串$S$的长度。
对于$10\%$的数据,$S$只由一种字符构成。
对于另$40\%$的数据,满足$|S|,Lleqslant 15$。
对于另$20\%$的数据,满足$|S|,Lleqslant 100$。
对于全部$100\%$的数据,满足$|S|,Lleqslant 3,000$。


题解

题看起来有点虎人,但是仔细一想并不难。

依然考虑$DP$。

设$dp[i][j]$表示处理完$S$的前$i$为,长度为$j$的本质不同的子序列的个数。

如果不考虑本质不同,那么转移将是$dp[i][j]=dp[i-1][j]+dp[i-1][j-1]$。

现在来考虑减去重复的部分。

设当前位$i$的字符为$s_i$,$s_i$的上一次出现的位置是$p$,那么以$s_p$结尾的串与算重的串一一对应。

那么我们就需要减去$dp[p-1][j-1]$。

于是,最终的状态转移方程是:$dp[i][j]=dp[i-1][j]+dp[i-1][j-1]-dp[p-1][j-1]$。

时间复杂度:$Theta({|S|}^2)$。

期望得分:$100$分。

实际得分:$100$分。


代码时刻

#include<bits/stdc++.h>
using namespace std;
char ch[3001];
int S[3001],L;
int pre[3001];
long long dp[3001][3001];
int main()
{
	scanf("%s%d",ch+1,&L);
	S[0]=strlen(ch+1);
	for(int i=1;i<=S[0];i++)
		S[i]=ch[i]-'a'+1;
	for(int i=1;i<=S[0];i++)
		for(int j=i-1;j;j--)
			if(S[i]==S[j]){pre[i]=j;break;}
	dp[0][0]=1;
	for(int i=1;i<=S[0];i++)
	{
		dp[i][0]=1;
		for(int j=1;j<=min(i,L);j++)
			dp[i][j]=(dp[i-1][j]+dp[i-1][j-1]-(pre[i]?dp[pre[i]-1][j-1]:0)+998244353)%998244353;
	}
	printf("%lld",dp[S[0]][L]);
	return 0;
}

rp++

原文地址:https://www.cnblogs.com/wzc521/p/11524399.html