Hard
题目描述
给你一个整数 n,请你帮忙统计一下我们可以按下述规则形成多少个长度为 n 的字符串:
- 字符串中的每个字符都应当是小写元音字母(‘a’, ’e’, ‘i’, ‘o’, ‘u’)
- 每个元音 ‘a’ 后面都只能跟着 ’e’
- 每个元音 ’e’ 后面只能跟着 ‘a’ 或者是 ‘i’
- 每个元音 ‘i’ 后面不能再跟着另一个 ‘i’
- 每个元音 ‘o’ 后面只能跟着 ‘i’ 或者是 ‘u’
- 每个元音 ‘u’ 后面只能跟着 ‘a’
由于答案可能会很大,所以请你返回 模 10^9 + 7 之后的结果。
示例 1:
输入:n = 1
输出:5
解释:所有可能的字符串分别是:"a", "e", "i", "o", "u"。
示例 2:
输入:n = 2
输出:10
解释:所有可能的字符串分别是:"ae", "ea", "ei", "ia", "ie", "io", "iu", "oi", "ou", "ua"。
示例 3:
输入:n = 5
输出:68
提示:
1 <= n <= 2 * 10^4
解题思路
这是一道典型的动态规划题目,核心思想是根据题目给出的转移规则来构建状态转移方程。
首先分析转移规则:
- ‘a’ 只能跟着 ’e’
- ’e’ 能跟着 ‘a’ 或 ‘i’
- ‘i’ 能跟着 ‘a’, ’e’, ‘o’, ‘u’(除了’i’)
- ‘o’ 能跟着 ‘i’ 或 ‘u’
- ‘u’ 只能跟着 ‘a’
我们可以用 dp[i][j] 表示长度为 i 且以第 j 个元音结尾的字符串数量,其中 j=0,1,2,3,4 分别对应 ‘a’,’e’,‘i’,‘o’,‘u’。
状态转移方程为:
dp[i][0] = dp[i-1][1] + dp[i-1][2] + dp[i-1][4](以’a’结尾,可由’e’,‘i’,‘u’转移而来)dp[i][1] = dp[i-1][0] + dp[i-1][2](以’e’结尾,可由’a’,‘i’转移而来)dp[i][2] = dp[i-1][1] + dp[i-1][3](以’i’结尾,可由’e’,‘o’转移而来)dp[i][3] = dp[i-1][2](以’o’结尾,只能由’i’转移而来)dp[i][4] = dp[i-1][2] + dp[i-1][3](以’u’结尾,可由’i’,‘o’转移而来)
初始状态:dp[1][j] = 1 对所有 j。
为了优化空间复杂度,我们可以使用滚动数组,只保存当前和前一个状态。
代码实现
class Solution {
public:
int countVowelPermutation(int n) {
const int MOD = 1000000007;
long long a = 1, e = 1, i = 1, o = 1, u = 1;
for (int len = 2; len <= n; len++) {
long long newA = (e + i + u) % MOD;
long long newE = (a + i) % MOD;
long long newI = (e + o) % MOD;
long long newO = i % MOD;
long long newU = (i + o) % MOD;
a = newA;
e = newE;
i = newI;
o = newO;
u = newU;
}
return (a + e + i + o + u) % MOD;
}
};
class Solution:
def countVowelPermutation(self, n: int) -> int:
MOD = 1000000007
a, e, i, o, u = 1, 1, 1, 1, 1
for length in range(2, n + 1):
new_a = (e + i + u) % MOD
new_e = (a + i) % MOD
new_i = (e + o) % MOD
new_o = i % MOD
new_u = (i + o) % MOD
a, e, i, o, u = new_a, new_e, new_i, new_o, new_u
return (a + e + i + o + u) % MOD
public class Solution {
public int CountVowelPermutation(int n) {
const int MOD = 1000000007;
long a = 1, e = 1, i = 1, o = 1, u = 1;
for (int len = 2; len <= n; len++) {
long newA = (e + i + u) % MOD;
long newE = (a + i) % MOD;
long newI = (e + o) % MOD;
long newO = i % MOD;
long newU = (i + o) % MOD;
a = newA;
e = newE;
i = newI;
o = newO;
u = newU;
}
return (int)((a + e + i + o + u) % MOD);
}
}
var countVowelPermutation = function(n) {
const MOD = 1000000007;
let a = 1, e = 1, i = 1, o = 1, u = 1;
for (let len = 2; len <= n; len++) {
let newA = (e + i + u) % MOD;
let newE = (a + i) % MOD;
let newI = (e + o) % MOD;
let newO = i % MOD;
let newU = (i + o) % MOD;
a = newA;
e = newE;
i = newI;
o = newO;
u = newU;
}
return (a + e + i + o + u) % MOD;
};
复杂度分析
| 复杂度类型 | 复杂度 | 说明 |
|---|---|---|
| 时间复杂度 | O(n) | 需要遍历从2到n的所有长度 |
| 空间复杂度 | O(1) | 只使用了常数个变量存储状态 |