题目介绍
小蓝特别喜欢单调递增的事物。 在一个字符串中,如果取出若干个字符,将这些字符按照在字符串中的顺
序排列后是单调递增的,则成为这个字符串中的一个单调递增子序列。 例如,在字符串 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
个小写英文字母,分四行显示):(如果你把 以下文字复制到文本文件中,请务必检查复制的内容是否与文档中的一致。在 试题目录下有一个文件
inc.txt,内容与下面的文本相同)
本质不同的递增子序列有多少个?
tocyjkdzcieoiodfpbgcncsrjbhmugdnojjddhllnofawllbhf
iadgdcdjstemphmnjihecoapdjjrprrqnhgccevdarufmliqij
gihhfgdcmxvicfauachlifhafpdccfseflcdgjncadfclvfmad
vrnaaahahndsikzssoywakgnfjjaihtniptwoulxbaeqkqhfwl
解题思路
-
动态规划,首先dp[i]的值代表 从s[]开始并且以s[i]为结尾的上升序列个数
-
因为每单独一个字母必然是一个上升序列,所以dp初始化为1
-
不难理解,dp[i]的值又全体dp[j]共同决定(0<j<i)
-
遍历j=0 ~ (i-1),若s[j]<s[i],dp[i]+=dp[j],因为有dp[j]个以s[j]结尾的子序列,因为s[i]>s[j], 在这些子序列末尾加上s[i]所得到的所有子序列都包含于dp[i]对应的序列集
若s[j]=s[i],dp[i]-=dp[j],按照dp的定义s[j]=s[i]时应该是dp[i]不改变,但是我们最终是求全体的序列个数,需要进行累加,如果不减去dp[j],会把dp[j]之前的情况重复累加
举个例子 1 2 7 8 3 7
dp[i] | dp的值 | 对应的元素 |
---|---|---|
dp[0] | 1 | 1 |
dp[1] | 2 | 2 12 |
dp[2] | 4 | 7 17 27 127 |
dp[3] | 8 | 8 18 28 78 128 178 278 1278 |
dp[4] | 4 | 13 13 23 123 |
dp[5] | 4 | 7 17 27 127 137 127 1237 37 |
按照dp的定义 7 17 27 127属于dp[5],因为他们的结尾都是7且都是上升序列,但是累加时这四个和dp[2]对应的序列集重复 因此需要在dp[5]-dp[2]
#include <iostream>
#include <string.h>
using namespace std;
#define N 200
int dp[N]={0};
int main()
{
string s="tocyjkdzcieoiodfpbgcncsrjbhmugdnojjddhllnofawllbhfiadgdcdjstemphmnjihecoapdjjrprrqnhgccevdarufmliqijgihhfgdcmxvicfauachlifhafpdccfseflcdgjncadfclvfmadvrnaaahahndsikzssoywakgnfjjaihtniptwoulxbaeqkqhfwl";
//string s="lanqiao";
for(int i=0;i<N;i++)dp[i]=1;
for(int i=1;i<N;i++)
{
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<N;i++)
{
ans+=dp[i];
}
cout<<ans<<endl;
//for(int i=0;i<N;i++)cout<<dp[i]<<" "<<endl;
return 0;
}