首页 > 其他分享 >【LeetCode 1220】统计元音字母序列的数目

【LeetCode 1220】统计元音字母序列的数目

时间:2024-03-23 16:55:23浏览次数:18  
标签:pre 1220 int res 字母 元音 LeetCode dp MOD

题目描述

原题链接: LeetCode.1220 统计元音字母序列的数目

解题思路

  • 定义DP数组dp[i][j]含义为长度为i+1且以j字符结尾的字符串有多少个, j从0到4依次代表('a', 'e', 'i', 'o', 'u')这5个元音字符, dp[0][0~4]长度为1时的初始个数都为1;
  • dp[i][j]对应字符串末尾字符已经由j确定, 对应个数就要看dp[i-1]中有哪些字符结尾的字符串能追加1个j字符得到, 按照题意总结递推公式:
    • 只有['e', 'i', 'u']的后面能跟着'a', 所以\(dp[i][0] = dp[i-1][1] + dp[i-1][2] + dp[i-1][4]\);
    • 只有['a', 'i']的后面能跟着'e', 所以\(dp[i][1] = dp[i-1][0] + dp[i-1][2]\);
    • 只有['e', 'o']的后面能跟着'i', 所以\(dp[i][2] = dp[i-1][1] + dp[i-1][3]\);
    • 只有['i']的后面能跟着'o', 所以\(dp[i][3] = dp[i-1][2]\);
    • 只有['i', 'o']的后面能跟着'u', 所以\(dp[i][4] = dp[i-1][2] + dp[i-1][3]\)。
  • 上述5维DP解法的时间复杂度仍然有\(O(n)\)规模, 对于这种公式固定的线性递推可以借助矩阵快速幂技巧得到更快速的解法:
    • 时间复杂度可以优化到\(O(5^3 * log_2n)\);
    • 由题意可以按行轻松确定递推矩阵的值:
      • 'a'后面只能跟着'e'可以确定第一行只有第二列为1;
      • 'e'后面只能跟着'a'或'i'可以确定第二行只有第一和第三列为1;
      • 'i'后面只禁止跟着'i'可以确定第三行除了第二列全是1;
      • 'o'后面只能跟着'i'或'u'可以确定第四行只有第三和第五列为1;
      • 'u'后面只能跟着'a'可以确定第五行只有第一列为1。
    • 由上述k维1阶递推公式也可以按列直接写出递推矩阵,每列的值都对应公式中的常数项1或0, 这里直接给出矩阵值对照理解一下:\(\begin{pmatrix} 0&1&0&0&0\\ 1&0&1&0&0\\ 1&1&0&1&1\\ 0&0&1&0&1\\ 1&0&0&0&0 \end{pmatrix}\)。

解题代码

  • 朴素5维1阶动态规划解法

      final int MOD = 1_000_000_007;
    
      /**
       * 按照规则总结5维DP的递推公式求解
       * 执行用时: 19 ms , 在所有 Java 提交中击败了 41.18% 的用户
       * 内存消耗: 43.46 MB , 在所有 Java 提交中击败了 36.98% 的用户
       */
      public int countVowelPermutation(int n) {
          // dp[i][j]: 表示长度为i+1且以j字母结尾的字符串的个数, 其中j=0, 1, 2, 3, 4依次表示'a', 'e', 'i', 'o', 'u'字母
          int[][] dp = new int[n][5];
          Arrays.fill(dp[0], 1);
          /*
          * 根据题意中规则, 总结这5个元音字母结尾时合规的上一位字母有哪些
          * dp[i]['a']: dp[i-1]['e'] + dp[i-1]['i'] + dp[i-1]['u']
          * dp[i]['e']: dp[i-1]['a'] + dp[i-1]['i']
          * dp[i]['i']: dp[i-1]['e'] + dp[i-1]['o']
          * dp[i]['o']: dp[i-1]['i']
          * dp[i]['u']: dp[i-1]['i'] + dp[i-1]['o']
          */
          for (int i = 1; i < n; i++) {
              int[] pre = dp[i - 1];
              dp[i][0] = ((pre[1] + pre[2]) % MOD + pre[4]) % MOD;
              dp[i][1] = (pre[0] + pre[2]) % MOD;
              dp[i][2] = (pre[1] + pre[3]) % MOD;
              dp[i][3] = pre[2];
              dp[i][4] = (pre[2] + pre[3]) % MOD;
          }
          int res = 0;
          for (int i = 0; i < 5; i++) {
              res = (res + dp[n - 1][i]) % MOD;
          }
          return res;
      }
    
  • 时间复杂度最优的矩阵快速幂解法

      final int MOD = 1_000_000_007;
    
      /**
       * 矩阵快速幂加速解法
       * 执行用时: 1 ms , 在所有 Java 提交中击败了 100.00% 的用户
       * 内存消耗: 39.81 MB , 在所有 Java 提交中击败了 58.06% 的用户
       */
      public int countVowelPermutation2(int n) {
          // 长度为1的元音字母序列个数各有1种
          int[][] start = {{1, 1, 1, 1, 1}};
          // 递推矩阵可以直接由题意按行确认, 每个字母后面允许跟着的字母对应列的值为1组成一行
          int[][] transfer = {
                  {0, 1, 0, 0, 0},
                  {1, 0, 1, 0, 0},
                  {1, 1, 0, 1, 1},
                  {0, 0, 1, 0, 1},
                  {1, 0, 0, 0, 0}};
          int[][] resMatrix = multiply(start, power(transfer, n - 1, MOD), MOD);
          long ans = 0;
          for (int num : resMatrix[0]) {
              ans = (ans + num) % MOD;
          }
          return (int) ans;
      }
    
      public int[][] power(int[][] base, int n, int mod) {
          int row = base.length;
          int[][] res = new int[row][row];
          for (int i = 0; i < row; i++) {
              res[i][i] = 1;
          }
          while (n > 0) {
              if ((n & 1) == 1) {
                  res = multiply(res, base, mod);
              }
              base = multiply(base, base, mod);
              n >>= 1;
          }
          return res;
      }
    
      public int[][] multiply(int[][] a, int[][] b, int mod) {
          int m = a.length, n = b[0].length;
          int[][] res = new int[m][n];
          int k = b.length;
          for (int aRow = 0; aRow < m; aRow++) {
              for (int bCol = 0; bCol < n; bCol++) {
                  long sum = 0;
                  for (int i = 0; i < k; i++) {
                      sum = (sum + (long) a[aRow][i] * b[i][bCol]) % mod;
                  }
                  res[aRow][bCol] = (int) sum;
              }
          }
          return res;
      }
    

标签:pre,1220,int,res,字母,元音,LeetCode,dp,MOD
From: https://www.cnblogs.com/coding-memory/p/18091308

相关文章

  • 代码随想录算法训练营day31 | leetcode 455. 分发饼干、376. 摆动序列、53. 最大子数
    目录贪心理论基础核心:题目链接:455.分发饼干-简单题目链接:376.摆动序列-中等题目链接:53.最大子数组和-中等贪心理论基础核心:由局部推全局最优题目链接:455.分发饼干-简单题目描述:假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。对每......
  • 【LeetCode 509 】斐波那契数
    题目描述原题链接:LeetCode.0509斐波那契数解题思路题目直接给出了公式,朴素解法可以直接用\(O(n)\)复杂度求出答案,可以看做是递归或动态规划的入门题;这里重点作为模板题来介绍矩阵快速幂技巧,讲一下\(O(log_2n)\)复杂度的解法:递推公式\(F(n)=F(n-1)+F(n-2)\),转换为矩......
  • (Java)猛刷LeetCode——数组知识点篇
    数组Array在连续的内存空间中,存储一组相同类型的元素元素:值索引:数组的下标数组访问(Access)和数组搜索(Search)●数组访问:索引●数组搜索:找2这个元素数组中有没有以下是数组的常规操作:数组创建、添加元素、访问元素、修改元素、删除元素、遍历数组、查找元素、数组......
  • LeetCode题练习与总结:接雨水
    一、题目给定 n个非负整数表示每个宽度为1的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。示例1:输入:height=[0,1,0,2,1,0,1,3,2,1,2,1]输出:6解释:上面是由数组[0,1,0,2,1,0,1,3,2,1,2,1]表示的高度图,在这种情况下,可以接6个单位的雨水(蓝色部分表示雨......
  • LeetCode题练习与总结:缺失的第一个正数
    一、题目给你一个未排序的整数数组nums,请你找出其中没有出现的最小的正整数。请你实现时间复杂度为O(n)并且只使用常数级别额外空间的解决方案。二、解题思路遍历数组:首先,我们需要遍历数组,找到所有负数和零,并将它们替换为一个特定的值(比如数组的最大值加一),这样我们就......
  • LeetCode 55.跳跃游戏
    题目:方法一:给定数组中,每一位都可以确定出他所能跳到的最远距离(nums[i]+i)当然,前提是当前该位能够由前面的位置跳到我们可以定义一个总的最远距离(maxdistance)来记录(最远距离:当前能够到达的最大下标值)如果当前位置能够被跳到且其所能跳到的最远距离大于maxdistance,那么更新......
  • LeetCode刷题记录——day4
    https://leetcode.cn/problems/trapping-rain-water/description/?envType=study-plan-v2&envId=top-interview-150对于一个可以构成“碗”的序列,最后装满水的话应该和最短的一边齐平,那么可以左右各遍历一次,记录每个元素位置对应的最短边高度,再对比就可以得出左右哪边最短class......
  • 【LeetCode-153.寻找旋转排序数组的最小值】
    已知一个长度为 n 的数组,预先按照升序排列,经由 1 到 n 次 旋转 后,得到输入数组。例如,原数组 nums=[0,1,2,4,5,6,7] 在变化后可能得到:若旋转 4 次,则可以得到 [4,5,6,7,0,1,2]若旋转 7 次,则可以得到 [0,1,2,4,5,6,7]注意,数组 [a[0],a[1],a[2],...,a[n-1......
  • 算法打卡day25|回溯法篇05|Leetcode 491.递增子序列、46.全排列、47.全排列 II
     算法题Leetcode491.递增子序列题目链接:491.递增子序列大佬视频讲解:递增子序列视频讲解 个人思路和昨天的子集2有点像,但昨天的题是通过排序,再加一个标记数组来达到去重的目的。而本题求自增子序列,是不能对原数组进行排序的,因为排完序的数组都是自增子序列了。解决......
  • 【C++ leetcode】双指针问题
    1.  611.有效三角形的个数题目给定一个包含非负整数的数组nums,返回其中可以组成三角形三条边的三元组个数。题目链接.-力扣(LeetCode)画图和文字分析判断是否是三角形要得到三边,由于遍历三边要套三层循环,时间复杂度很大,所以这里我们需要借助双指针思想,可......