[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|,L\leqslant 15$。
对于另$20\%$的数据,满足$|S|,L\leqslant 100$。
对于全部$100\%$的数据,满足$|S|,L\leqslant 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++