493.连续子数组零尾数问题 | 豆包MarsCode AI 刷题

13 阅读4分钟

问题描述

小F正在研究一个数组,并想要计算出其中的连续子数组的某种特性。给定一个整数数组,你需要编写一个函数来返回乘积末尾零的数量大于等于 x 的连续子数组的数量。

由于答案可能非常大,你需要将结果对 10^9 + 7 取模后再返回。

问题理解

我们需要计算数组中连续子数组的数量,这些子数组的乘积末尾零的数量大于等于 x。末尾零的数量取决于乘积中因子 2 和 5 的数量,取两者的最小值。

解题思路

  1. 计算因子数量:首先,我们需要计算数组中每个元素的因子 2 和 5 的数量。
  2. 前缀和:使用前缀和数组来记录从数组开头到当前位置的因子 2 和 5 的累计数量。
  3. 子数组遍历:遍历所有可能的子数组,并利用前缀和数组计算每个子数组的因子 2 和 5 的数量。
  4. 末尾零的计算:对于每个子数组,计算其末尾零的数量,并与 x 进行比较。
  5. 结果统计:统计满足条件的子数组数量,并对结果取模 10^9 + 7

具体步骤

  1. 计算每个数的因子数量

    • 编写一个函数 count_factors(num),用于计算一个数中因子 2 和 5 的数量。
  2. 构建前缀和数组

    • 创建两个前缀和数组 prefix_count2 和 prefix_count5,分别记录从数组开头到当前位置的因子 2 和 5 的累计数量。
  3. 遍历子数组

    • 使用双层循环遍历所有可能的子数组 [i, j]
    • 利用前缀和数组计算子数组 [i, j] 的因子 2 和 5 的数量。
  4. 计算末尾零的数量

    • 对于每个子数组,计算其末尾零的数量 min(count2, count5)
    • 如果末尾零的数量大于等于 x,则结果加1。
  5. 结果取模

    • 对结果取模 10^9 + 7,以防止溢出。

代码实现

def solution(a: list, x: int) -> int:
    MOD = 10**9 + 7

    def count_factors(num):
        count2, count5 = 0, 0
        while num % 2 == 0:
            num //= 2
            count2 += 1
        while num % 5 == 0:
            num //= 5
            count5 += 1
        return count2, count5

    prefix_count2 = [0] * (len(a) + 1)
    prefix_count5 = [0] * (len(a) + 1)

    for i in range(len(a)):
        count2, count5 = count_factors(a[i])
        prefix_count2[i + 1] = prefix_count2[i] + count2
        prefix_count5[i + 1] = prefix_count5[i] + count5

    result = 0

    for i in range(len(a)):
        for j in range(i, len(a)):
            count2 = prefix_count2[j + 1] - prefix_count2[i]
            count5 = prefix_count5[j + 1] - prefix_count5[i]

            zeros = min(count2, count5)

            if zeros >= x:
                result = (result + 1) % MOD

    return result

关键步骤解释

  1. count_factors 函数:用于计算一个数中因子 2 和 5 的数量。
  2. 前缀和数组prefix_count2 和 prefix_count5 分别记录从数组开头到当前位置的因子 2 和 5 的累计数量。
  3. 双层循环:遍历所有可能的子数组,并利用前缀和数组计算每个子数组的因子 2 和 5 的数量。
  4. 末尾零的计算:通过 min(count2, count5) 计算末尾零的数量,并与 x 进行比较。

时间复杂度

当前代码的主要时间复杂度来自于以下几个部分:

  1. 计算每个数的因子数量

    • 对于每个数,计算因子 2 和 5 的数量。这个操作的时间复杂度是 O(log n),其中 n 是数组中的最大值。
    • 由于数组中有 n 个数,因此这部分的总时间复杂度是 O(n log n)
  2. 构建前缀和数组

    • 遍历数组并计算前缀和数组。这个操作的时间复杂度是 O(n)
  3. 遍历所有可能的子数组

    • 使用双层循环遍历所有可能的子数组 [i, j]。这个操作的时间复杂度是 O(n^2)
    • 对于每个子数组,计算因子 2 和 5 的数量,这个操作的时间复杂度是 O(1)

综合以上部分,总的时间复杂度是 O(n log n) + O(n) + O(n^2)。由于 O(n^2) 是最高阶项,因此总的时间复杂度是 O(n^2)

空间复杂度

当前代码的主要空间复杂度来自于以下几个部分:

  1. 前缀和数组

    • prefix_count2 和 prefix_count5 分别记录从数组开头到当前位置的因子 2 和 5 的累计数量。这两个数组的大小都是 n + 1
    • 因此,这部分的空间复杂度是 O(n)
  2. 其他变量

    • 其他变量(如 count2count5zeros 等)的空间复杂度是 O(1)

综合以上部分,总的空间复杂度是 O(n)

总结

  • 时间复杂度O(n^2)
  • 空间复杂度O(n)