题目来源:https://leetcode-cn.com/problems/count-vowels-permutation/
大致题意:
给一个长度 n,求出五种元音字母在给定条件下能组成的不同字符串的个数
条件为:
- a 后面只能跟 e
- e 后面只能跟 i 或者 a
- i 后面不能跟 i
- o 后面只能跟 i 或者 u
- u 后面只能跟 a
思路
根据题目中的条件,如果能确定当前结尾的字符,那么就可以知道下一位的几种可能
那么反过来想,如果先确定了下一位的字符,那么前一位的可能也可以从条件中推出:
- a 前面只能是 e、 i、 u
- e 前面只能是 a、 i
- i 前面只能是 e、 o
- o 前面只能是 i
- u 前面只能是 i、 o
于是,若已知长度为 x ,结尾为各类字符的字符串个数,那么就可以求出长度为 x+1,结尾为各类字符的字符串个数
显然,是一个状态的迭代过程,可以用动态规划
####动态规划
- 使用 dp[i][j] 表示长度为 i 以字符 j 结尾的字符串个数,实际计算时使用索引 0~5 来代表 a ~ u
- 初始时 dp[0][j] 都为 1
- 然后根据上述的规则,递归计算 n - 1 次,中间结果可能过大,需要取余
- 最后返回 dp[n - 1][j] 的和
实际计算时,因为当前状态仅仅与上一个状态有关,所以可以只存储当前状态和上一个状态即可
代码:
public int countVowelPermutation(int n) {
// 需要用 long,否则会越界
long[][] dp = new long[2][5];
int MOD = 1000000007;
// 初始化
for (int i = 0; i < 5; i++) {
dp[0][i] = 1;
}
// 迭代 n-1 次
for (int i = 1; i < n; i++) {
// 新状态所在索引
int newIdx = i % 2;
// 旧状态所在索引
int oldIdx = newIdx == 0 ? 1 : 0;
dp[newIdx][0] = (dp[oldIdx][1] + dp[oldIdx][2] + dp[oldIdx][4]) % MOD;
dp[newIdx][1] = (dp[oldIdx][0] + dp[oldIdx][2]) % MOD;
dp[newIdx][2] = (dp[oldIdx][1] + dp[oldIdx][3]) % MOD;
dp[newIdx][3] = dp[oldIdx][2];
dp[newIdx][4] = (dp[oldIdx][2] + dp[oldIdx][3]) % MOD;
}
long ans = 0;
// 求和
for (int i = 0; i < 5; i++) {
ans = (ans + dp[(n - 1) % 2][i]) % MOD;
}
return (int)ans;
}