串(dp)

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

题目描述

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

输入描述:

一个正整数nnn(2≤n≤1062 \leq n \leq 10^62≤n≤106)

输出描述:

一个正整数,为满足条件的字符串数量对109+710^9+7109+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的思路, 才感觉茅塞顿开.
思路如下,先写出暴力的递归函数:
串(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 }
暴力思路

显然对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]

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

串(dp)
 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代码

 

 
上一篇:给四六级作文加分的优美句型


下一篇:10day 字符集优化 重点