1641 统计字典序元音字符串的数目(递归、递推)

1. 问题描述:

给你一个整数 n,请返回长度为 n 、仅由元音 (a, e, i, o, u) 组成且按 字典序排列的字符串数量。字符串 s 按字典序排列需要满足:对于所有有效的 i,s[i] 在字母表中的位置总是与 s[i+1] 相同或在s[i+1] 之前。

示例 1:

输入:n = 1
输出:5
解释:仅由元音组成的 5 个字典序字符串为 ["a","e","i","o","u"]

示例 2:

输入:n = 2
输出:15
解释:仅由元音组成的 15 个字典序字符串为
["aa","ae","ai","ao","au","ee","ei","eo","eu","ii","io","iu","oo","ou","uu"]
注意,"ea" 不是符合题意的字符串,因为 'e' 在字母表中的位置比 'a' 靠后

示例 3:

输入:n = 33
输出:66045

提示:

  • 1 <= n <= 50 

2. 思路分析:

① 分析题目可以知道这是一个组合问题,对于当前的字母c,按照字典序的规则尝试与其他的字母进行组合并且将其组成长度为n的字符串,我们的任务是需要统计出长度为n按照字典序排列的字符串的数目,所以本质上是尝试所有字符串的组合的问题,因为是尝试所有的可能性所以是可以使用递归来解决的(递归可以搜索出所有的可能性),我们在递归的时候可以传递一个记录当前正在递归的字符的位置,这样在下一次递归的时候也是从这个位置开始递归(当前的字母c是可以与自己进行组合的),这样就可以保持字典序排列的要求,并且对于每一个当前的字母都可以尝试与当前字母以及后面的字母进行组合所以需要在for循环中进行递归,使用递归求解的思路还是比较好理解的

② 除了①中比较好理解的递归思想之外,在力扣的评论区中发现还可以找出其中的规律来解决,可以发现如下的公式:(初始化的时候a = e = i = o = u = 1),本质上还是递推

例如长度为2的以a, e, i , o ,u开头的字符串的组合如下:

aa ae ai ao au

ee ei eo eu

ii io iu

oo ou

uu

而长度为3的字符串是在长度为2的基础上根据末尾字母进行添加的:

而当长度加1的时候又是在上一次这些字母结尾的基础上进行添加的,所以是可以转化为二维dp来解决的(由上一个状态递推出当前的状态),dp[i][j]表示以当前字母i开头的长度为j的字符串的数目,我们可以在for循环中进行递推即可

3. 代码如下:

class Solution:
    res = 0

    def recursion(self, n: int, pos: int, length: int):
        if length == n:
            self.res += 1
            return
        # 从当前位置开始递归尝试组合当前位置以及之后的字母
        for i in range(pos, 5):
            self.recursion(n, i, length + 1)

    def countVowelStrings(self, n: int) -> int:
        self.recursion(n, 0, 0)
        return self.res

记录形成的字符串(可以在方法中传递一个变量来记录字符串的形成过程):

class Solution:
    res = 0

    def recursion(self, n: int, pos: int, length: int, s: str, cur: str):
        if length == n:
            self.res += 1
            # print(cur)
            return
        for i in range(pos, 5):
            self.recursion(n, i, length + 1, s, cur + s[i])

    def countVowelStrings(self, n: int) -> int:
        self.recursion(n, 0, 0, "aeiou", "")
        return self.res
class Solution:
    def countVowelStrings(self, n: int) -> int:
        a, e, i, o, u = 1, 1, 1, 1, 1
        # 长度为n - 1
        for k in range(n - 1):
            a = a + e + i + o + u
            e = e + i + o + u
            i = i + o + u
            o = o + u
            u = u
        # 长度为n
        return a + e + i + o + u

动态规划:

class Solution:
    def countVowelStrings(self, n: int) -> int:
        # 下面是根据找规律得到的二维dp的解法
        # dp[c][k]数组的含义是以字母c开头长度为k的字符串的数目
        dp = [[0] * n for i in range(5)]
        for i in range(5): dp[i][0] = 1
        for i in range(1, n):
            dp[0][i] = dp[0][i - 1] + dp[1][i - 1] + dp[2][i - 1] + dp[3][i - 1] + dp[4][i - 1]
            dp[1][i] = dp[1][i - 1] + dp[2][i - 1] + dp[3][i - 1] + dp[4][i - 1]
            dp[2][i] = dp[2][i - 1] + dp[3][i - 1] + dp[4][i - 1]
            dp[3][i] = dp[3][i - 1] + dp[4][i - 1]
            dp[4][i] = dp[4][i - 1]
        return dp[0][n - 1] + dp[1][n - 1] + dp[2][n - 1] + dp[3][n - 1] + dp[4][n - 1]

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值