来源:力扣(LeetCode)
描述:
给你一个整数 n
,请返回长度为 n
、仅由元音 (a
, e
, i
, o
, u
) 组成且按 字典序排列 的字符串数量。
字符串 s
按 字典序排列 需要满足:对于所有有效的 i
,s[i]
在字母表中的位置总是与 s[i+1]
相同或在 s[i+1]
之前。
示例 1:
输入:n = 1
输出:5
解释:仅由元音组成的 5 个字典序字符串为 ["a","e","i","o","u"]
示例 2:
输入:n = 2
输出:15
解释:仅由元音组成的 15 个字典序字符串为
["aa","ae","ai","ao","au","ee","ei","eo","eu","ii","io","iu","oo","ou","uu"]
注意,"ea" 不是符合题意的字符串,因为 'e' 在字母表中的位置比 'a' 靠后
示例 3:
输入:n = 33
输出:66045
提示:
方法一:动态规划
分别使用数字 0,1,2,3,4 代表元音字符 ‘a’,‘e’,‘i’,‘o’,‘u’。记 dp[i][j] 表示长度为 i + 1,以 j 结尾的按字典序排列的字符串数量,那么状态转移方程如下:
因此长度为 n 的按字典序排列的字符串数量为 ∑ k = 0 4 \sum_{k=0}^4 ∑k=04dp[n−1][j]。因为 dp[i] 的计算只涉及 dp[i−1] 部分的数据,同时 dp[i] 等价于 dp[i−1] 的前缀和,我们可以只使用一维数组进行存储,同时在一维数组进行原地修改。
代码:
class Solution {
public:
int countVowelStrings(int n) {
vector<int> dp(5, 1);
for (int i = 1; i < n; i++) {
for (int j = 1; j < 5; j++) {
dp[j] += dp[j - 1];
}
}
return accumulate(dp.begin(), dp.end(), 0);
}
};
执行用时:0 ms, 在所有 C++ 提交中击败了100.00%的用户
内存消耗:5.8 MB, 在所有 C++ 提交中击败了72.77%的用户
复杂度分析
时间复杂度:O(n×Σ),其中 n 是字符串的长度,Σ=5 表示元音字符集大小。
空间复杂度:O(Σ)。
方法二:组合数学
对于一个按字典序排列的元音字符串,假设 ‘a’,‘e’,‘i’,‘o’,‘u’ 的起始下标分别为 ia,ie,ii,io,iu,显然 ia = 0 且 0 ≤ ie ≤ ii ≤ io ≤ iu ≤ n。因此字典序元音字符串的数目等于满足 0 ≤ ie ≤ ii ≤ io ≤ iu ≤ n 的 (ie,ii,io,iu) 的取值数目。想要直接求得 (ie,ii,io,iu) 的取值数目是十分困难的,我们可以作以下转换:
由 0 ≤ ie ≤ ii ≤ io ≤ iu ≤ n 可知 0 ≤ i′e ≤ i′i ≤ i′o ≤ i′u ≤ n + 3。每一个 (ie,ii,io,iu) 都唯一地对应一个 (i′e,i′i,i′o,i′u),因此 (ie,ii,io,iu) 的取值数目等于 (i′e,i′i,i′o,i′u) 的取值数目。(i′e,i′i,i′o,i′u) 等价于从 n + 4 个数中选取互不相等的 4 个数,因此 (i′e,i′i,i′o,i′u) 的取值数目等于组合数 C n + 4 4 C_{n+4}^4 Cn+44。
代码:
class Solution {
public:
int countVowelStrings(int n) {
return (n + 1) * (n + 2) * (n + 3) * (n + 4) / 24;
}
};
执行用时:0 ms, 在所有 C++ 提交中击败了100.00%的用户
内存消耗:5.8 MB, 在所有 C++ 提交中击败了75.32%的用户
复杂度分析
时间复杂度:O(Σ),其中 Σ=5 表示元音字符集大小。
空间复杂度:O(1)。
author:LeetCode-Solution