1220-统计元音字母序列的数目
给你一个整数 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
方法一:动态规划
思路
题目中给定的字符的下一个字符的规则如下:
- 字符串中的每个字符都应当是小写元音字母 (\texttt{
a'}, \texttt{
e’}, \texttt{i'}, \texttt{
o’}, \texttt{`u’}); - 每个元音
a' 后面都只能跟着
e’; - 每个元音
e' 后面只能跟着
a’ 或者是 `i’; - 每个元音
i' 后面不能再跟着另一个
i’; - 每个元音
o' 后面只能跟着
i’ 或者是 `u’; - 每个元音
u' 后面只能跟着
a’;
以上等价于每个字符的前一个字符的规则如下:
- 元音字母
a' 前面只能跟着
e’}, \texttt{i'}, \texttt{
u’; - 元音字母
e' 前面只能跟着
a’}, \texttt{`i’; - 每个元音
i' 前面只能跟着
e’}, \texttt{`o’; - 每个元音
o' 前面只能跟着
i’; - 每个元音
u' 后面只能跟着
o’}, \texttt{`i’;
我们设 dp}[i][j] 代表当前长度为 i 且以字符 j 为结尾的字符串的数目,其中在此 j = {0,1,2,3,4 分别代表元音字母 {\texttt{a'}, \texttt{
e’}, \texttt{i'}, \texttt{
o’}, \texttt{`u’},通过以上的字符规则,我们可以得到动态规划递推公式如下:
\left{
\begin{array}{lr}
\textit{dp}[i][0] = \textit{dp}[i-1][1] + \textit{dp}[i-1][2] + \textit{dp}[i-1][4] \
\textit{dp}[i][1] = \textit{dp}[i-1][0] + \textit{dp}[i-1][2] \
\textit{dp}[i][2] = \textit{dp}[i-1][1] + \textit{dp}[i-1][3] \
\textit{dp}[i][3] = \textit{dp}[i-1][2] \
\textit{dp}[i][4] = \textit{dp}[i-1][2] + \textit{dp}[i-1][3]
\end{array}
\right.
按照题目规则最终可以形成长度为 n 的字符串的数目为:\sum_{i=0}^4\textit{dp}[n][i]
- 实际计算过程中只需要保留前一个状态即可推导出后一个状态,计算长度为 i 的状态只需要长度为 i-1 的中间变量即可,i-1 之前的状态全部都可以丢弃掉。计算过程中,答案需要取模 1\text{e}9+7。
代码
1 | class Solution { |
1 | class Solution { |
1 | public class Solution { |
1 | class Solution: |
1 | typedef long long LL; |
1 | func countVowelPermutation(n int) (ans int) { |
1 | var countVowelPermutation = function(n) { |
复杂度分析
时间复杂度:O(C \times n),其中 n 是给定,C 表示元音字母的数量,在本题中 C = 5。
空间复杂度:O(C),我们只需要常数个空间存储不同组的数目。
方法二:矩阵快速幂
思路
已经知道上述的递推公式,可以转将其转化为矩阵乘法,设 f(n) 表示长度为 n 的字符串且以不同元音字母为结尾的组合数矩阵,构造矩阵的递推关系如下:
f(n) =
\begin{bmatrix}
0 & 1 & 1 & 0 & 1\
1 & 0 & 1 & 0 & 0\
0 & 1 & 0 & 1 & 0\
0 & 0 & 1 & 0 & 0\
0 & 0 & 1 & 1 & 0\
\end{bmatrix}
\times f(n-1)
因此我们可以推出:
f(n) =
\begin{bmatrix}
0 & 1 & 1 & 0 & 1\
1 & 0 & 1 & 0 & 0\
0 & 1 & 0 & 1 & 0\
0 & 0 & 1 & 0 & 0\
0 & 0 & 1 & 1 & 0\
\end{bmatrix}^{n-1}
\times f(1)
令:
f(1) =
\begin{bmatrix}
1 \
1 \
1 \
1 \
1 \
\end{bmatrix}
M =
\begin{bmatrix}
0 & 1 & 1 & 0 & 1\
1 & 0 & 1 & 0 & 0\
0 & 1 & 0 & 1 & 0\
0 & 0 & 1 & 0 & 0\
0 & 0 & 1 & 1 & 0\
\end{bmatrix}
因此只要我们能快速计算矩阵 M 的 n 次幂,就可以得到 f(n) 的值。如果直接求取 M^n ,时间复杂度是 O(n),可以定义矩阵乘法,然后用快速幂算法来加速 M^n 的求取。计算过程中,答案需要取模 1\text{e}9+7。
代码
1 | class Solution { |
1 | using LL = long long; |
1 | public class Solution { |
1 | import numpy as np |
1 | typedef long long LL; |
1 | const mod int = 1e9 + 7 |
复杂度分析
时间复杂度:O(C^3 \log n),其中 n 是给定的参数,C 表示元音字母的数量,在本题中 C = 5,每次矩阵相乘的时间复杂度为 O(C^3),最多需要 \log n 次矩阵相乘。
空间复杂度:O(C^2),需要保空间来存储矩阵乘法的结果。