LeetCode1220. 统计元音字母序列的数目—动态规划法和矩阵幂表示

题目概述

在这里插入图片描述
题目链接:点我做题

题解

一、动态规划

  字符串类的题如果要考虑动态规划都可以考虑以什么东西结尾的目标量,比如本题,先定义一个映射 a − 0 , e − 1 , i − 2 , o − 3 , u − 4 a-0,e-1,i-2,o-3,u-4 a0,e1,i2,o3,u4定义 f ( i , j ) ( j = 0 , 1 , 2 , 3 , 4 ) f(i,j)(j=0,1,2,3,4) f(i,j)(j=0,1,2,3,4)表示以字符j结尾的长度为i的串在本题规则下的数量,下面我们分析一下规则:

  规则一直在告诉我们后面后面,但其实这是一个误导,我们反而应该考虑前面(也应了我们的假设),下面考虑以j结尾的字符前面都有可能有哪些情况:
  以 j = 0 j=0 j=0为例,以a结尾的字符,前面可不可以是a呢,不可以,第一条规则说a后面只能跟着e;可不可以是e呢,第二条规则告诉我们可以;前面可不可以是i呢,可以,规则3只规定了i后面不能跟i,其他没有限制;前面可不可以是o呢,不可以,第四条规则规定o后面只能是i或u;前面能不能是u呢,可以,最后一条规则规定u后面只能跟a。
  同理可以把其他字符前面允许的字符规定分析出来:
a 前 : e , i , u e 前 : a , i i 前 : e , o o 前 : i u 前 : i , o a前:e,i,u\\ e前:a,i\\ i前:e,o\\ o前:i\\ u前:i,o\\ a:e,i,ue:a,ii:e,oo:iu:i,o
  长度为i的,以j结尾的串的数量不就等于去掉j字符后,长度为i-1,结尾字符为j字符允许的前缀的字符串的数量嘛,所以有状态转移方程:
a : f ( i , 0 ) = f ( i − 1 , 1 ) + f ( i − 1 , 2 ) + f ( i − 1 , 4 ) e : f ( i , 1 ) = f ( i − 1 , 0 ) + f ( i − 1 , 2 ) i : f ( i , 2 ) = f ( i − 1 , 1 ) + f ( i − 1 , 3 ) o : f ( i , 3 ) = f ( i − 1 , 2 ) u : f ( i , 4 ) = f ( i − 1 , 2 ) + f ( i − 1 , 3 ) a:f(i,0)=f(i-1,1)+f(i-1,2)+f(i-1,4)\\ e:f(i,1)=f(i-1,0)+f(i-1,2)\\ i:f(i,2)=f(i-1,1)+f(i-1,3)\\ o:f(i,3)=f(i-1,2)\\ u:f(i,4)=f(i-1,2)+f(i-1,3)\\ a:f(i,0)=f(i1,1)+f(i1,2)+f(i1,4)e:f(i,1)=f(i1,0)+f(i1,2)i:f(i,2)=f(i1,1)+f(i1,3)o:f(i,3)=f(i1,2)u:f(i,4)=f(i1,2)+f(i1,3)
  显然有初始条件 f ( 0 , i ) = 0 f(0,i) = 0 f(0,i)=0, f ( 1 , i ) = 1 f(1,i)=1 f(1,i)=1
  接下来进行动态规划就可以解决了,用一个二维数组dp储存计算过的状态就可以了,注意题中说的要% 1 e 9 + 7 1e9+7 1e9+7

class Solution {
public:
    int countVowelPermutation(int n) 
    {
        vector<vector<long long>> dp(n + 1, vector<long long>(5, 0));
        long long mod = 1e9 + 7;
        for (int i = 0; i < 5; i++)
        {
            dp[1][i] = 1;
        }
        for (int i = 2; 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 long ret = 0;
        for (int i = 0; i < 5; ++i)
        {
            ret += dp[n][i];
        }
        return ret % (mod);
    }
};

时间复杂度: O ( C n ) O(Cn) O(Cn),C为元音字符的个数。
空间复杂度:本题的状态只和前置状态有关,我们可以只记录上一轮的状态而不必开辟dp数组,所以空间复杂度其实可以优化到O©,C为元音字符个数。

二、矩阵法

  观察我们的状态转移方程:
a : f ( i , 0 ) = f ( i − 1 , 1 ) + f ( i − 1 , 2 ) + f ( i − 1 , 4 ) e : f ( i , 1 ) = f ( i − 1 , 0 ) + f ( i − 1 , 2 ) i : f ( i , 2 ) = f ( i − 1 , 1 ) + f ( i − 1 , 3 ) o : f ( i , 3 ) = f ( i − 1 , 2 ) u : f ( i , 4 ) = f ( i − 1 , 2 ) + f ( i − 1 , 3 ) a:f(i,0)=f(i-1,1)+f(i-1,2)+f(i-1,4)\\ e:f(i,1)=f(i-1,0)+f(i-1,2)\\ i:f(i,2)=f(i-1,1)+f(i-1,3)\\ o:f(i,3)=f(i-1,2)\\ u:f(i,4)=f(i-1,2)+f(i-1,3)\\ a:f(i,0)=f(i1,1)+f(i1,2)+f(i1,4)e:f(i,1)=f(i1,0)+f(i1,2)i:f(i,2)=f(i1,1)+f(i1,3)o:f(i,3)=f(i1,2)u:f(i,4)=f(i1,2)+f(i1,3)
若定义向量 f ( i ) ⃗ = ( f ( i , 0 ) , f ( i , 1 ) , f ( i , 2 ) , f ( i , 3 ) , f ( i , 4 ) ) T \vec{f(i)}=(f(i,0),f(i,1),f(i,2),f(i,3),f(i,4))^{T} f(i) =(f(i,0),f(i,1),f(i,2),f(i,3),f(i,4))T,考虑矩阵知识,状态转移方程可以写为:
f ( i ) ⃗ = [ 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 ] f ( i − 1 ) ⃗ = A f ( i − 1 ) ⃗ \vec{f(i)}=\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}\vec{f(i-1)}=A\vec{f(i-1)} f(i) =0100010100110110010110000f(i1) =Af(i1)
f ( 1 ) ⃗ = ( 1 , 1 , 1 , 1 , 1 ) T \vec{f(1)}=(1,1,1,1,1)^{T} f(1) =(1,1,1,1,1)T,要得到 f ( n ) ⃗ \vec{f(n)} f(n) 只要让f(1)乘 A n − 1 A^{n-1} An1即可.求矩阵的幂有一些快一点的数学算法,比如快速幂算法可以使用。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值