第十一届蓝桥杯 本质上升序列

试题 D: 本质上升序列

本题总分: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 个小写英文字母,分四行显示):(如果你把以下文字复制到文本文件中,请务必检查复制的内容是否与文档中的一致。在试题目录下有一个文件 inc.txt,内容与下面的文本相同)
tocyjkdzcieoiodfpbgcncsrjbhmugdnojjddhllnofawllbhf
iadgdcdjstemphmnjihecoapdjjrprrqnhgccevdarufmliqij
gihhfgdcmxvicfauachlifhafpdccfseflcdgjncadfclvfmad
vrnaaahahndsikzssoywakgnfjjaihtniptwoulxbaeqkqhfwl
本质不同的递增子序列有多少个?

【答案提交】
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一
个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。


答案:3616159


解析

使用动态规划:
设str为题目中的目标字符串,定义dp[i]为以str[i]为结尾时的递增子序列的个数。
考虑j<i,遍历j=0~i-1,则有如下讨论:

  1. str[j] < str[i],此时dp[i]=dp[i]+dp[j]
  2. str[j] = str[i],此时dp[i]=dp[i]-dp[j]
  3. str[j] > str[i],此时是降序,不考虑

为了观察方便,就以数字2、1、3、8、5、7、4、5为例:

strdp[i]dp的值递增子序列
2dp[0]12
1dp[1]11
3dp[2]33, 23, 13
8dp[3]68, 28, 18, 38, 238, 138
5dp[4]65, 25, 15, 35, 235, 135
7dp[5]127, 27, 17, 37, 237, 137, 57, 257, 157, 357, 2357, 1357
4dp[6]64, 24, 14, 34, 234, 134
5dp[7]65, 25, 15, 35, 235, 135, 45, 245, 145, 345, 2345, 1345

由上面的表格可知,当str[j]=str[i]时,会出现重复的子序列,此时就要减去这一部分,也就是1. str[j] < str[i],此时dp[i]=dp[i]+dp[j]的讨论。实际上由于是遍历j=0~i-1,故而有如下公式,这个公式和前面的1.2.3处的讨论是等价的:
在这里插入图片描述

程序

#include <bits/stdc++.h>
using namespace std;

int main()
{
    string str = "tocyjkdzcieoiodfpbgcncsrjbhmugdnojjddhllnofawllbhfiadgdcdjstemphmnjihecoapdjjrprrqnhgccevdarufmliqijgihhfgdcmxvicfauachlifhafpdccfseflcdgjncadfclvfmadvrnaaahahndsikzssoywakgnfjjaihtniptwoulxbaeqkqhfwl";
    int result = 0;
    int dp[str.size()];
    for(int i=0; i<str.size(); i++) 
        dp[i]=1;

    for(int i=0; i<str.size(); i++)
        for(int j=0; j<i; j++)
            if(str[j] < str[i])
                dp[i] += dp[j];
            else if(str[j] == str[i]) 
                dp[i] -= dp[j];

    for(int i=0; i<str.size(); i++)
        result += dp[i];
    cout << "result = " << result << '\n';

    return 0;
}

结果

yocin@ubuntu:~/Documents/cppPractice$ ./main
result = 3616159
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值