蓝桥杯——本质上升序列


问题描述

小蓝特别喜欢单调递增的事物。
在一个字符串中,如果取出若干个字符,将这些字符按照在字符串中的顺序排列后是单调递增的,则成为这个字符串中的一个单调递增子序列。
例如,在字符串 lanqiao 中,如果取出字符 n 和 q,则 nq 组成一个单调递增子序列。类似的单调递增子序列还有 lnq、i、ano 等等。
小蓝发现,有些子序列虽然位置不同,但是字符序列是一样的,例如取第二个字符和最后一个字符可以取到 ao,取最后两个字符也可以取到 ao。小蓝认为他们并没有本质不同。
对于一个字符串,小蓝想知道,本质不同的递增子序列有多少个?
例如,对于字符串 lanqiao,本质不同的递增子序列有 21 个。它们分别
是 l、a、n、q、i、o、ln、an、lq、aq、nq、ai、lo、ao、no、io、lnq、
anq、lno、ano、aio。
请问对于以下字符串

tocyjkdzcieoiodfpbgcncsrjbhmugdnojjddhllnofawllbhfiadgdcdjstemphmnjihecoapdjjrprrqnhgccevdarufmliqijgihhfgdcmxvicfauachlifhafpdccfseflcdgjncadfclvfmadvrnaaahahndsikzssoywakgnfjjaihtniptwoulxbaeqkqhfwl

本质不同的递增子序列有多少个?

解题思路

第一种

先看一组例子,试图找出点规律。

a: a
ab: a,b,ab
abc: a,b,ab,c, ac,bc,abc
abcd:a,b,ab,c,ac,bc,abc,d,ad,bd,cd,abd,acd,bcd,abcd

用dp(a)表示字母a结尾的本质不同的递增子序列个数
dp(a)=1,dp(b)=2,p©=4,dp(d)=8
以此类推
可得:dp(x)=dp(a)+…+dp(x-1)+1
而总共的递增子序列的个数为:dp(a)+dp(b)+dp©+dp(d)=15

考虑重复字母的影响

a: a
ac: a,c,ac
acb: a,c,ac,b,ab
acbc: a,c,ac,b,ab,bc,abc
acbcb: a,c,ac,b,ab,bc,abc

dp(a)=1,dp(c1)=2,dp(b1)=2,dp(c2)=4,dp(b2)=2
=>dp(c1)=dp(a)+1=2,dp(b1)=dp(a)+1=2,dp(c2)=dp(a)+dp(b1)+1=4,dp(b2)=dp(a)+1=2
由于c和b出现了两次,我们只要最后一次的值就好,因为后面出现的b和c能组成的子序列包含了前面b和c能出现的子序列
而总共的递增子序列的个数为:dp(a)+dp(b2)+dp(c2)=7

代码

#include<iostream>
using namespace std;
int dp[205];//共200个小写英文字母 
int main(){
	string s;
	cin>>s;
	for(int i=0;i<s.length();i++){
		int use[26]={0};//标志字母是否重复出现 
		dp[i]=1; //递推得出+1 
		for(int j=i-1;j>=0;j--){//一定是从右到左,不然会漏 
			//与前面没有重复的,而且前面的字母要比现在遍历的字母小 
			if(s[j]<s[i] && !use[s[j]-'a']){
				dp[i]+=dp[j];
				use[s[j]-'a']=1; 
			}
		} 
	}
	int use[26]={0};
	int ans=0;
	for(int i=s.length()-1;i>=0;i--){
		if(!use[s[i]-'a']){ //除去重复的子序列
			ans+=dp[i];
			use[s[i]-'a']=1;
		}
	} 
	cout<<ans<<endl; //3616159
	return 0;
} 

第二种

写出递推公式

if(s[i]>s[j]) 
	dp[i]+=dp[j]; //前字母小,加上
else if(s[i]==s[j]) 
	dp[i]-=dp[j]; //重复的就删掉

代码

#include<iostream>
using namespace std;
int dp[205];//共200个小写英文字母 
int main(){
	string s;
	cin>>s;
	for(int i=0;i<s.length();i++){
		dp[i]=1;
		for(int j=0;j<i;j++){
			if(s[i]>s[j]){
				dp[i]+=dp[j];
			}else if(s[i]==s[j]){
				dp[i]-=dp[j];
			}
		}
	}
	int ans=0;
	for(int i=0;i<s.length();i++){
		ans+=dp[i];
	}
	cout<<ans;
	return 0; 
} 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

relizi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值