• 串(dp)


    时间限制:C/C++ 1秒,其他语言2秒
    空间限制:C/C++ 262144K,其他语言524288K
    64bit IO Format: %lld

    题目描述

    长度不超过$n$,且包含子序列us的、只由小写字符构成的字符串有多少个? 答案对$10^9+7$取模。
    所谓子序列,指一个字符串删除部分字符(也可以不删)得到的字符串。
    例如,"unoacscc"包含子序列"us",但"scscucu"则不包含子序列"us"

    输入描述:

    一个正整数$n(2 <= n <= 10^6)$

    输出描述:

    一个正整数,为满足条件的字符串数量对$10^9+7$取模的值

    示例1

    输入

    复制

    $2$

    输出

    复制

    $1$

    说明

    仅有“us”这一个字符串合法
    示例2

    输入

    复制

    $3$

    输出

    复制

    $77$

    说明

    长度为3的字符串里,
    形状是"u?s"的共有26个
    形状是"?us"的共有26个
    形状是"us?"的共有26个。
    但是,"uss"和"uus"被各多计算了1次,应该减去,
    所以共有26*3-2=76个。
    再加上长度为2的"us",所以长度不超过3的合法字符串共有77个。
    示例3

    输入

    复制

    $874520$

    输出

    复制

    $16471619$

    看到n的范围跟结果大小, 确定是dp, 可惜我dp太弱了, 一直不知道该如何下手, 直到昨天学了一下递归改dp的思路, 才感觉茅塞顿开.
    思路如下,先写出暴力的递归函数:
     1 ll ans;
     2 void dfs(int cur, bool u, bool s)
     3 {
     4     ans += u && s;
     5     if (cur == n)
     6         return;
     7     for(int i = 0; i < 26; ++ i){
     8         dfs(cur + 1, u || 'a' + i == 'u', u && (s || 'a' + i == 's'));
     9     }
    10 }
    暴力思路

    显然对$2 <= n <= 1e6$的范围来说这个算法会超时, 可以从参数里面发现, 这三个状态都是我们要转移的状态.

    所以可以确定我们要改的dp数组中有三个状态, 即$dp[n][u][s]$, n为字符串长度, u跟s表示当前字符串中是否包括了u或者s字符

    首先我们根据题目可以知道, 长度为1的时候是不可能包含字符串"us"的, 但可以包含一个字符, 其中有1个字符'u'和其他25个字符.

    所以我们可以得知$dp[1][1][0] = 1$( 长度为1的字符串中包含字符'u'的情况只有一种), $dp[1][0][0] = 25$ (长度为1的字符串中不包含u的情况, 即有25种不同的字符, 所以这里的值是25)

     

    之后就是如何通过初始状态转移了.

    在包含字符'u'跟字符's'的字符串中, 字符's'出现的位置一定要在字符'u'出现的位置之后, 这是题目中描述的有效的字符串的要求.

    我们可以知道, 在字符串后面加上一个字符, 有三种情况,

    1. 在不含字符'u'跟字符's'的字符串中, 在末尾添加一个字符'u', 总共有$dp[n - 1][0][0]$种情况, 状态转移到$dp[n][1][0]$上; 在末尾添加除'u'以外的字符, 总共有$25 * dp[n - 1][0][0]$种情况, 状态转移到$dp[n][0][0]$上.
    2. 在包含字符'u'但不包含字符's'的字符串中, 添加一个字符's', 总共有$dp[n - 1][1][0]$种情况, 状态转移到$dp[n][1][1]$上; 在末尾添加除's'以外的字符, 总共有$25 * dp[n - 1][1][0]$种情况, 状态转移到$dp[n][1][0]$上.
    3. 在包含字符'u'且包含字符's'的字符串中, 添加任意字符, 都将转移到$dp[n][1][1]$上, 总共有$26 * dp[n - 1][1][1]$种情况.

    所以我们可以从中总结出状态转移方程

    $dp[n][1][1] = dp[n][1][0] + 26 * dp[n][1][1]$

    $dp[n][1][0] = dp[n][0][0] + 25 * dp[n][1][0]$

    $dp[n][0][0] = 25 * dp[n][0][0]$

    至此, 我们可以根据上述状态转移方程轻松写出代码.

     1 #include <iostream>
     2 #include <algorithm>
     3 #include <cmath>
     4 #include <string>
     5 using namespace std;
     6 typedef long long ll;
     7 constexpr int mod = 1e9 + 7;
     8 
     9 constexpr int MAXN = 1e6 + 7;
    10 int n;
    11 ll dp[MAXN][2][2]; //下标表示长度, dp[n]表示长度为n的字符串中带us的字符串有多少个
    12 
    13 ll ans;
    14 int main()
    15 {
    16     ll ans = 0;
    17     dp[1][0][0] = 25;
    18     dp[1][1][0] = 1;
    19     // dp[2] = 1;
    20     cin >> n;
    21     // dfs(0, false, false);
    22     for (int i = 2; i <= n; ++i)
    23     {
    24 
    25         dp[i][1][1] = (dp[i - 1][1][0] + 26 * dp[i - 1][1][1] % mod) % mod;
    26         ans = (ans + dp[i][1][1]) % mod;
    27         dp[i][1][0] = (dp[i - 1][0][0] + 25 * dp[i - 1][1][0] % mod) % mod;
    28         dp[i][0][0] = dp[i - 1][0][0] * 25 % mod;
    29     }
    30     cout << ans << endl;
    31     return 0;
    32 }
    AC代码
     
     
  • 相关阅读:
    《生命3.0—在亿年的尺度下审视生命的演进》阅读笔记3
    软件杯赛题周总结(2)
    《生命3.0—在亿年的尺度下审视生命的演进》阅读笔记2
    记一次阅读源码的小经历
    11
    解决在 CSS 中,如何实现动态吸顶的样式/效果 ?
    Angular 初始化项目后,如何把默认的 .css 文件修改为 .scss 文件?
    解决 Angular 项目中,添加 <router-outlet> 标签后,报错: ‘router-outlet’ is not a known element 的问题。
    在 Angular 项目中,如何为项目单独创建路由文件?
    [NOIP2013 提高组] 《火柴排队》
  • 原文地址:https://www.cnblogs.com/daremo/p/15045731.html
Copyright © 2020-2023  润新知