等和子数组问题 | 豆包MarsCode AI刷题

36 阅读5分钟

问题分析

给定一个长度为 n 的数组 A,我们需要找出所有连续子数组,并计算每个子数组中不同元素的个数(称为“权值”)。然后,我们需要统计每个可能的权值(从 1 到 n)对应的子数组数量。

举个例子,如果数组为 [1, 2, 2, 3],我们需要计算所有子数组的不同元素个数,并输出每个不同权值(1, 2, 3,…)的子数组数量。

思路解析

  1. 权值定义

    • 对于一个子数组,权值是子数组中不同元素的个数。
    • 例如,子数组 [1, 2, 2, 3] 的权值为 3,因为它有三个不同的元素 {1, 2, 3}
  2. 暴力枚举方法

    • 一个简单的思路是,枚举所有可能的子数组。对于每一个子数组,计算它的不同元素的个数,然后统计不同权值的子数组数量。
  3. 如何高效实现

    • 我们可以通过双重循环来枚举所有的连续子数组。外层循环遍历子数组的起始位置,内层循环遍历结束位置。在遍历过程中,我们使用一个集合 seen 来记录当前子数组中的不同元素,从而快速得到子数组的权值。
    • 每遇到一个新的元素,就将其加入集合中,集合的大小即为当前子数组的权值。

解题步骤

  1. 初始化计数器

    • 创建一个长度为 n+1 的数组 c 来记录每个权值的子数组数量。c[i] 表示权值为 i 的子数组数量。
  2. 双重循环枚举所有子数组

    • 外层循环遍历子数组的起始位置 i
    • 内层循环遍历从起始位置 i 到数组末尾的所有子数组的结束位置 j
    • 对于每个子数组,我们使用集合 seen 来记录不同的元素,集合的大小即为当前子数组的权值。每次遇到一个新的子数组时,更新对应的计数器。
  3. 返回结果

    • 最终返回数组 c[1:],即从权值 1 到 n 的子数组数量。

代码实现

python
复制代码
def solution(n: int, a: list) -> list:
    # 用于记录每个权值的子数组个数
    c = [0 for _ in range(n + 1)]
    
    # 枚举所有的子数组
    for i in range(n):
        seen = set()  # 用来记录当前子数组的不同元素
        for j in range(i, n):
            seen.add(a[j])  # 加入当前元素
            c[len(seen)] += 1  # 记录当前子数组的权值
    
    return c[1:]  # 返回从1到n的子数组数量

# 测试用例
if __name__ == '__main__':
    # 示例 1: 权值为 [1, 2, 3, 4] 的子数组数量
    print(solution(4, [1, 2, 2, 3]) == [5, 4, 1, 0])
    # 示例 2: 权值为 [1, 2, 3] 的子数组数量
    print(solution(3, [1, 1, 1]) == [6, 0, 0])
    # 示例 3: 权值为 [1, 2, 3, 4, 5] 的子数组数量
    print(solution(5, [1, 2, 3, 2, 1]) == [5, 5, 5, 0, 0])

解题思路详解

  1. 初始化

    • 我们首先创建一个 c 数组,用来记录每个权值的子数组数量。c[i] 存储的是权值为 i 的子数组的数量。注意,数组的大小是 n+1,因为权值从 1 到 n,所以我们需要包括 n 这个位置。
  2. 双重循环

    • 外层循环 i 遍历所有可能的子数组的起始位置。
    • 内层循环 j 从位置 i 开始,遍历到数组的末尾。对于每一个子数组,我们用 seen 集合记录当前子数组中出现的不同元素。
    • 每遇到一个新的元素,就把它加入 seen 集合。此时,len(seen) 即为当前子数组的权值,我们更新计数器 c[len(seen)]
  3. 返回结果

    • 最后,返回 c[1:],即排除掉权值为 0 的计数器。c[i] 表示权值为 i 的子数组数量。

复杂度分析

  • 时间复杂度

    • 外层循环遍历了 n 次,内层循环从每个起始位置 i 向后遍历,最多遍历 n 次。因此,整体的时间复杂度是 O(n^2)
    • 对于每个子数组,我们使用集合来记录不同元素的个数,集合的操作(插入、查找)是 O(1) 时间复杂度。因此,总体时间复杂度是 O(n^2)
  • 空间复杂度

    • 我们需要一个 seen 集合来记录当前子数组的不同元素,最坏情况下 seen 的大小为 n,因此空间复杂度是 O(n)
    • c 数组的空间复杂度是 O(n),因此总体空间复杂度是 O(n)

示例

  1. 示例 1

    • 输入:[1, 2, 2, 3]

    • 输出:[5, 4, 1, 0]

    • 解释:

      • 权值为 1 的子数组有 5 个:[1][2][2][3][2, 2]
      • 权值为 2 的子数组有 4 个:[1, 2][2, 2][2, 3][1, 2, 2]
      • 权值为 3 的子数组有 1 个:[1, 2, 2, 3]
      • 权值为 4 的子数组没有。
  2. 示例 2

    • 输入:[1, 1, 1]

    • 输出:[6, 0, 0]

    • 解释:

      • 权值为 1 的子数组有 6 个:[1][1][1][1, 1][1, 1][1, 1, 1]
      • 权值为 2 和 3 的子数组没有。

总结

通过枚举所有连续子数组并使用集合来记录不同元素,我们能够在 O(n^2) 的时间复杂度内完成这个问题的求解。此方法直观且易于理解,适合处理较小规模的输入。如果问题规模更大,可能需要进一步优化。