统计同位异构字符串数目

标签: 哈希表 数学 字符串 组合数学 计数

难度: Hard

给你一个字符串 s ,它包含一个或者多个单词。单词之间用单个空格 ' ' 隔开。

如果字符串 t 中第 i 个单词是 s 中第 i 个单词的一个 排列 ,那么我们称字符串 t 是字符串 s 的同位异构字符串。

  • 比方说,"acb dfe" 是 "abc def" 的同位异构字符串,但是 "def cab" 和 "adc bef" 不是。

请你返回 s 的同位异构字符串的数目,由于答案可能很大,请你将它对 109 + 7 取余 后返回。

示例 1:

输入:s = "too hot"
输出:18
解释:输入字符串的一些同位异构字符串为 "too hot" ,"oot hot" ,"oto toh" ,"too toh" 以及 "too oht" 。

示例 2:

输入:s = "aa"
输出:1
解释:输入字符串只有一个同位异构字符串。

提示:

  • 1 <= s.length <= 105
  • s 只包含小写英文字母和空格 ' ' 。
  • 相邻单词之间由单个空格隔开。

Submission

运行时间: 118 ms

内存: 17.2 MB

class Solution:
    def countAnagrams(self, s: str) -> int:
        res = 1
        for word in s.split(" "):
            n, count = len(word), collections.Counter(word)
            for w in count:
                res *= math.comb(n, count[w])
                res %= 10 ** 9 + 7
                n -= count[w]
        return res

Explain

此题解利用了组合数学的原理来计算每个单词的异构字符串数目。对于每个单词,我们首先计算每个字符的出现次数。然后,我们利用组合公式 C(n, k) 来计算每个字符能构成的异构字符串数目,并将这些数目相乘得到该单词的异构字符串总数。最后,我们将所有单词的异构字符串总数相乘得到整个字符串的异构字符串数目。

时间复杂度: O(n^2)

空间复杂度: O(n)

class Solution:
    def countAnagrams(self, s: str) -> int:
        res = 1
        for word in s.split(' '):
            n, count = len(word), collections.Counter(word)
            for w in count:
                res *= math.comb(n, count[w])
                res %= 10 ** 9 + 7
                n -= count[w]
        return res

Explore

在题解中,每个单词中的字符首先被统计其出现次数。利用组合公式 C(n, k),计算一个字符可以在不同位置出现的组合数。例如,单词中有 n 个位置,若某个字符出现 k 次,则这个字符在单词中的排列组合数为 C(n, k)。对于单词中的每个字符,计算完这种组合后,n 需要减去这个字符的出现次数(因为这些位置已被占用),然后对下一个字符使用更新后的 n 继续计算。这样,对于单词中的所有字符,他们的组合数乘积给出了该单词的所有可能的异构字符串数目。

在计算大数的组合数时,结果很快就会非常大,可能导致计算机处理这些大数时出现溢出错误。使用模数 10^9+7 进行取模操作是常见的技术,主要是因为 10^9+7 是一个大的质数,这有助于减小计算时的冲突和保持结果的稳定。此外,取模操作还可以保持数值在一个可管理的范围内,避免因数值过大而导致的性能问题。

进行取模操作不会影响最终的计算结果的正确性,因为模运算满足分配律。即,(a * b) % c = [(a % c) * (b % c)] % c。因此,每次计算后进行取模可以保证结果始终在模数范围内,避免溢出。关于效率,虽然取模操作本身需要额外计算,但它可以防止数字过大,从而加快其他数学运算的速度,总体上是提升了效率。

Python的 `collections.Counter` 用于快速统计各元素的数量,其内部实现基于哈希表,因此能在平均O(n)时间内完成对元素的计数,这对于算法性能是非常有利的。`math.comb` 函数用于计算组合数 C(n, k),它可能使用递归方法或更优化的迭代方法来计算阶乘和组合,确保计算效率。这些函数的使用大大简化了代码的复杂度并提升了执行效率,使得处理大量数据或复杂计算成为可能。