这是我参与2022首次更文挑战的第14天,活动详情查看:2022首次更文挑战
题目
给你一个整数 n,请你帮忙统计一下我们可以按下述规则形成多少个长度为 n 的字符串:
字符串中的每个字符都应当是小写元音字母('a', 'e', 'i', 'o', 'u')
- 每个元音
'a'后面都只能跟着'e' - 每个元音
'e'后面只能跟着'a'或者是'i' - 每个元音
'i'后面 不能 再跟着另一个'i' - 每个元音
'o'后面只能跟着'i'或者是'u' - 每个元音
'u'后面只能跟着'a'由于答案可能会很大,所以请你返回 模10^9 + 7之后的结果。
示例
输入: n = 1
输出: 5
解释: 所有可能的字符串分别是:"a", "e", "i" , "o" 和 "u"。
输入:n = 2
输出:10
解释:所有可能的字符串分别是:"ae", "ea", "ei", "ia", "ie", "io", "iu", "oi", "ou" 和 "ua"。
输入: n = 5
输出: 68
提示
1 <= n <= 2 * 10^4
解题思路
动态规划
这道题咋一看有点像有限状态机,每一个元音后面都有对应的限制,只能跟着符合条件的字母,但是当使用有限状态机的解法来解题时会发现有些别扭,该题最终要求的是根据规则可以形成多少个字符串,而不是要判断是否符合条件,因此我们需要在其基础上做个改进。
定义一个二维dp数组,用来保存每一个长度,各个元音字母分别有多少组合。
- 当
n == 1时,分别自有元音字母本身,所以需要将对应的元素填充为1 - 根据规则,当长度增加是,对应的元音字母组合也需要增加。
但是这里需要注意的是不能直接照着规则,例如:元音
'a'后面都只能跟着'e',不能写成dp[i][a] += dp[i - 1][e],这么写的话顺序就反过来了。
我们需要看的是后面的条件,字母'e'为'a','i'这两个字母的后缀条件,那么就可以理解为'e'前面必定为'a'或者'i',那么在字母'e'前,则可能有'a'种情况加上'i'种情况,因此我们需要加上两者的和,即可求得当前字母所有的排列。
最后再对五个字母进行求和操作即可得出最终结果。
class Solution {
public int countVowelPermutation(int n) {
final int MOD = (int)1e9+7;
long[][] dp = new long[n][5];
Arrays.fill(dp[0], 1);
for(int i = 1; i < n; ++i){
dp[i][0] += (dp[i - 1][1] + dp[i - 1][2] + dp[i - 1][4]) % MOD;
dp[i][1] += (dp[i - 1][0] + dp[i - 1][2]) % MOD;
dp[i][2] += (dp[i - 1][1] + dp[i - 1][3]) % MOD;
dp[i][3] += (dp[i - 1][2]) % MOD;
dp[i][4] += (dp[i - 1][2] + dp[i - 1][3])% MOD;
}
long sum = 0L;
for(int i = 0; i < 5; ++i){
sum += dp[n - 1][i] ;
}
return (int)(sum % MOD);
}
}