力扣 1220. 统计元音字母序列的数目

79 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

题目来源:leetcode-cn.com/problems/co…

大致题意: 给一个长度 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,结尾为各类字符的字符串个数

显然,是一个状态的迭代过程,可以用动态规划

动态规划

  1. 使用 dp[i][j] 表示长度为 i 以字符 j 结尾的字符串个数,实际计算时使用索引 0~5 来代表 a ~ u
  2. 初始时 dp[0][j] 都为 1
  3. 然后根据上述的规则,递归计算 n - 1 次,中间结果可能过大,需要取余
  4. 最后返回 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;
    }