hihocoder #24 Rikka with Subsequence

探讨了一个关于字符串操作及子序列计数的数学问题,通过动态规划算法求解特定条件下字符串子序列个数的期望值,并提供了解题思路与C++实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目2 : Rikka with Subsequence

时间限制:10000ms
单点时限:1000ms
内存限制:256MB

描述

众所周知,萌萌哒六花不擅长数学,所以勇太给了她一些数学问题做练习,其中有一道是这样的:

勇太有一个长度为n的字符串s,现在他对这个字符串进行了一些操作,在操作之后第i个字符有Ai%的概率被删除。现在他想要知道在操作之后得到的新的字符串的本质不同的子序列个数的期望值(包括空串)。

字符串s是字符串t的子序列当且仅当删除了t中若干个位置之后可以得到字符串s。

当然,这个问题对于萌萌哒六花来说实在是太难了,你可以帮帮她吗?

输入

第一行输入一个正整数n。

第二行输入一个长度为n的只包含小写字母的字符串。

第二行输入n个空格隔开的数字表示数组A。

n ≤ 5 × 105, 0 ≤ Ai ≤ 100

输出

输出一个整数表示答案对998244353取模后的值。

额外样例

输入
输出

4
abdb
50 50 50 50

311951365



样例输入
4
abdb
0 0 0 0
样例输出
14
首先我们知道当ai=0时的做法:用dp[i]表示s[1~i]的子序列的个数,则 dp[i]=dp[i-1]+dp[i-2]+...+dp[last[s[i]] ](last[s[i]]表示上一个s[i]出现的位置) 若ai>0,也可以这样做,即: dp[i]=(1-a[i])*(dp[i-1]+dp[i-2]+..+dp[第一个s[i]]+(a[第一个s[i]])*(dp.....))
程序如下:
#include<cstdio>
#include<cstdlib>
#include<cstring>
#define maxn 500100
#define mod 998244353
int qpow(int a,int b,int p){
	int tmp=a,ans=1;
	for(int i=0;b>>i;i++,tmp=1ll*tmp*tmp%p)
		if((b>>i)&1)ans=1ll*ans*tmp%p;
	return ans;
}
int dp[maxn],a[maxn],fa[maxn],n,ans,last[26];
char str[maxn];
int main(){
	scanf("%d%s",&n,str+1);
	int inx=qpow(100,mod-2,mod);
	for(int i=1;i<=n;++i){
		scanf("%d",&a[i]);
		a[i]=1ll*(100-a[i])*inx%mod;
	}
	dp[0]=1;
	for(int i=0;i<26;++i)last[i]=1;
	for(int i=1;i<=n;++i){
		dp[i]=1ll*last[str[i]-'a']*a[i]%mod;
		for(int j=0;j<26;++j)if(j+'a'!=str[i])
			last[j]=(last[j]+dp[i])%mod;
		ans=(ans+dp[i])%mod;
	}
	printf("%d",(ans+1+mod)%mod);
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值