📌  相关文章
📜  排列单词以使所有元音一起出现的方式的数量(1)

📅  最后修改于: 2023-12-03 15:25:55.520000             🧑  作者: Mango

题目介绍

这道题目要求我们将给定字符串中的元音字母(即 a, e, i, o, u )排列在一起,求这样排列的方式数量。例如,对于字符串 "leetcode",一种满足要求的排列是 "eeo" + "l" + "tcod",问求出这样排列的方式数量。

解题思路

这道题可以使用一个递归函数来解决,每次递归时选择一个元音字母作为当前组合的起点,然后必须依次选择剩下的元音字母进行排列,直到所有元音字母都被选择了为止。由于元音字母只有五个,因此实际上可以使用一个 bitmask 来代表当前已经被选择的元音字母,每次递归新开一个 bitmask,然后使用一个循环枚举下一个被选择的元音字母即可。

具体来说,我们可以使用状态压缩的方式将当前已经被选择的元音字母保存为一个 bitmask,其中 1 表示已经被选择,0 表示未被选择。时间复杂度为 O(2^5 * n),其中 n 是字符串长度。

代码实现

下面给出 Python 代码实现:

class Solution:
    def countVowelPermutation(self, n: int) -> int:
        MOD = 10 ** 9 + 7
        
        # 构建字母相邻关系矩阵
        to = [
            [1], # a -> e
            [0, 2], # e -> a, i
            [0, 1, 3, 4], # i -> a, e, o, u
            [2, 4], # o -> i, u
            [0] # u -> a
        ]
        
        # 递归函数进行组合计数
        @functools.cache
        def dfs(pos: int, vowel: int) -> int:
            if pos == n:
                return 1
            
            res = 0
            for nxt in to[vowel]:
                res = (res + dfs(pos+1, nxt)) % MOD
            
            return res
        
        # 枚举所有起点进行计数
        ans = 0
        for i in range(5):
            ans = (ans + dfs(1, i)) % MOD
        
        return ans

其中,函数 dfs() 表示从 pos 位置开始,以 vowel 为起点进行组合计数。使用 functools.cache 装饰器可以对递归函数进行记忆化,避免重复计算。代码中还给出了一个 5 x 5 的矩阵 to,表示元音字母之间的相邻关系,可以帮助我们枚举下一个元音字母。最后,对所有五个元音字母进行枚举,将计数结果相加即可得到答案。

总结

本题是一道非常典型的使用递归+记忆化的组合计数问题,思路比较巧妙。需要注意的是,由于元音字母只有五个,因此 bitmask 的长度应该是 5。另外,在需要使用模运算的地方,要注意取模的时机,避免出现计算错误。