小A的子数组权值 | 豆包MarsCode AI刷题

46 阅读4分钟

小A的子数组权值

一、问题描述

小A拿到了一个长度为 nnn 的数组,并且定义了一个连续子数组的“权值”为该子数组内不同元素的个数。现在,她想知道,权值分别为 1,2,3,…,n1, 2, 3, \dots, n1,2,3,…,n 的子数组数量有多少个。

换句话说,给定一个数组,我们需要输出一个包含 nnn 个整数的数组,第 iii 个数表示权值为 iii 的子数组数量。

二、思路解析

  1. 理解问题:
  • 对于一个给定的数组,我们可以通过枚举所有的连续子数组来计算其“权值”。
  • 每个子数组的权值即是该子数组内不同元素的个数。例如,对于子数组 [1, 2, 2, 3],它的权值是 3,因为其中有三个不同的元素 {1, 2, 3}
  • 目标是找到所有权值为 1,2,3,…,n1, 2, 3, \dots, n1,2,3,…,n 的子数组数量。
  1. 暴力枚举方法:

一种直接的做法是枚举数组的所有子数组,对于每个子数组,计算其不同元素的个数,并统计权值为 1,2,3,…,n1, 2, 3, \dots, n1,2,3,…,n 的子数组个数。

三、具体做法:

  • 我们可以通过一个双重循环,遍历所有可能的连续子数组。外层循环表示子数组的起始位置,内层循环遍历子数组的结束位置。
  • 对于每个子数组,我们使用一个集合 seen 来记录已经出现过的元素。集合的大小即为子数组的权值。
  • 然后我们更新每个权值对应的计数器。
  1. 时间复杂度:
  • 外层循环需要遍历 nnn 个起始位置。
  • 内层循环遍历每个起始位置后的所有元素,因此内层循环的最大长度为 nnn。
  • 对于每个子数组,计算其不同元素的个数需要 O(nnn) 的时间(因为我们使用集合来记录不同元素)。
  • 因此,整体的时间复杂度为 O(n2n^2n2),对于大部分输入来说是可接受的。

解题代码

ini
 代码解读
复制代码
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. 初始化:

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

    • 外层循环 i 遍历子数组的起始位置。
    • 内层循环 j 遍历从 i 开始的所有子数组的结束位置。
    • 每次遇到一个新的元素时,将其加入集合 seen 中。seen 是一个集合,它能自动去重,因此 len(seen) 即为当前子数组的权值。
    • 对于每个子数组,增加对应权值的计数器。
  3. 返回结果:

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

五、复杂度分析

  • 时间复杂度:O(n2n^2n2),因为我们需要枚举所有的子数组,对于每个子数组,更新集合的操作是 O(1),但总体的循环次数是 n2n^2n2。

  • 空间复杂度:O(nnn),我们使用了一个集合 seen 来记录当前子数组的不同元素,最多存储 nnn 个元素,因此空间复杂度是 O(nnn)。