小A的子数组权值
一、问题描述
小A拿到了一个长度为 nnn 的数组,并且定义了一个连续子数组的“权值”为该子数组内不同元素的个数。现在,她想知道,权值分别为 1,2,3,…,n1, 2, 3, \dots, n1,2,3,…,n 的子数组数量有多少个。
换句话说,给定一个数组,我们需要输出一个包含 nnn 个整数的数组,第 iii 个数表示权值为 iii 的子数组数量。
二、思路解析
- 理解问题:
- 对于一个给定的数组,我们可以通过枚举所有的连续子数组来计算其“权值”。
- 每个子数组的权值即是该子数组内不同元素的个数。例如,对于子数组
[1, 2, 2, 3],它的权值是 3,因为其中有三个不同的元素{1, 2, 3}。 - 目标是找到所有权值为 1,2,3,…,n1, 2, 3, \dots, n1,2,3,…,n 的子数组数量。
- 暴力枚举方法:
一种直接的做法是枚举数组的所有子数组,对于每个子数组,计算其不同元素的个数,并统计权值为 1,2,3,…,n1, 2, 3, \dots, n1,2,3,…,n 的子数组个数。
三、具体做法:
- 我们可以通过一个双重循环,遍历所有可能的连续子数组。外层循环表示子数组的起始位置,内层循环遍历子数组的结束位置。
- 对于每个子数组,我们使用一个集合
seen来记录已经出现过的元素。集合的大小即为子数组的权值。 - 然后我们更新每个权值对应的计数器。
- 时间复杂度:
- 外层循环需要遍历 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])
四、解题思路详解
-
初始化:
- 创建一个长度为 n+1n + 1n+1 的数组
c来记录不同权值的子数组数量。c[i]表示权值为 iii 的子数组个数。
- 创建一个长度为 n+1n + 1n+1 的数组
-
双重循环枚举子数组:
- 外层循环
i遍历子数组的起始位置。 - 内层循环
j遍历从i开始的所有子数组的结束位置。 - 每次遇到一个新的元素时,将其加入集合
seen中。seen是一个集合,它能自动去重,因此len(seen)即为当前子数组的权值。 - 对于每个子数组,增加对应权值的计数器。
- 外层循环
-
返回结果:
- 最后,返回
c[1:],即从权值 1 到 nnn 的子数组数量。
- 最后,返回
五、复杂度分析
-
时间复杂度:O(n2n^2n2),因为我们需要枚举所有的子数组,对于每个子数组,更新集合的操作是 O(1),但总体的循环次数是 n2n^2n2。
-
空间复杂度:O(nnn),我们使用了一个集合
seen来记录当前子数组的不同元素,最多存储 nnn 个元素,因此空间复杂度是 O(nnn)。