本题总分:10 分
【问题描述】
小蓝特别喜欢单调递增的事物。
在一个字符串中,如果取出若干个字符,将这些字符按照在字符串中的顺序排列后是单调递增的,则成为这个字符串中的一个单调递增子序列。
例如,在字符串 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。
请问对于以下字符串(共 200 个小写英文字母,分四行显示):
tocyjkdzcieoiodfpbgcncsrjbhmugdnojjddhllnofawllbhf
iadgdcdjstemphmnjihecoapdjjrprrqnhgccevdarufmliqij
gihhfgdcmxvicfauachlifhafpdccfseflcdgjncadfclvfmad
vrnaaahahndsikzssoywakgnfjjaihtniptwoulxbaeqkqhfwl
思路:
这题的思路比较清晰,通过动态规划来做。
从前往后开始求,后面的递增序列数依赖于前面的数。
分下面几种情况:
- a——1
- ac——1+2(1表示a,2表示c本身和ac)
- acd(d>前几个字母)——1+2+4(4表示d本身和ad、cd、acd)
- acdb(b小于前两个字母)——1+2+4+2(2表示b本身和ab)
- acdc(c等于前面其中一个字母)——1+2+4+1-1(从后往前,先+1表示本身,后-1表示遇到和自己相同的)
大致可以看出,
当前字母的序列数就是,1+大于前面几项字母的序列数的和。
如果当前字母和前面字母有重复的,则先+1,后-1。
所以,用数组值保存下每一个字母自己的序列数是关键!
这就是动态规划的思想。
看完了相信大致能够理解了。
代码如下:
#include<stdio.h>
#include<iostream>
#include<cstring>
using namespace std;
enum{
cd=200, //字母个数
};
long long mysum(long long[],int cd);
int main()
{
string s="tocyjkdzcieoiodfpbgcncsrjbhmugdnojjddhllnofawllbhf"
"iadgdcdjstemphmnjihecoapdjjrprrqnhgccevdarufmliqij"
"gihhfgdcmxvicfauachlifhafpdccfseflcdgjncadfclvfmad"
"vrnaaahahndsikzssoywakgnfjjaihtniptwoulxbaeqkqhfwl";
long long num[cd]={0}; //每个当前字母的序列数
int i;
int j;
for(i=0;i<cd;i++)
{
//先+1表示自己本身
num[i]++;
for(j=i-1;j>-1;j--){
//如果和前面字母有相同的情况,-1
if(s[j]==s[i]){
num[i]--;
break;
}
//如果大于前面某个字母,加上它的序列数
if(s[i]>s[j]){
num[i]+=num[j];
}
}
}
printf("%lld\n",mysum(num,cd));
return 0;
}
//求和得到结果
long long mysum(long long num[],int cd)
{
long long res=0;
for(int i=0;i<cd;i++){
res=res+num[i];
}
return res;
}
答案:3616159
菜鸟博主,希望多多指正~