问题分析
给定一个长度为 n 的数组 A,我们需要找出所有连续子数组,并计算每个子数组中不同元素的个数(称为“权值”)。然后,我们需要统计每个可能的权值(从 1 到 n)对应的子数组数量。
举个例子,如果数组为 [1, 2, 2, 3],我们需要计算所有子数组的不同元素个数,并输出每个不同权值(1, 2, 3,…)的子数组数量。
思路解析
-
权值定义:
- 对于一个子数组,权值是子数组中不同元素的个数。
- 例如,子数组
[1, 2, 2, 3]的权值为3,因为它有三个不同的元素{1, 2, 3}。
-
暴力枚举方法:
- 一个简单的思路是,枚举所有可能的子数组。对于每一个子数组,计算它的不同元素的个数,然后统计不同权值的子数组数量。
-
如何高效实现:
- 我们可以通过双重循环来枚举所有的连续子数组。外层循环遍历子数组的起始位置,内层循环遍历结束位置。在遍历过程中,我们使用一个集合
seen来记录当前子数组中的不同元素,从而快速得到子数组的权值。 - 每遇到一个新的元素,就将其加入集合中,集合的大小即为当前子数组的权值。
- 我们可以通过双重循环来枚举所有的连续子数组。外层循环遍历子数组的起始位置,内层循环遍历结束位置。在遍历过程中,我们使用一个集合
解题步骤
-
初始化计数器:
- 创建一个长度为
n+1的数组c来记录每个权值的子数组数量。c[i]表示权值为i的子数组数量。
- 创建一个长度为
-
双重循环枚举所有子数组:
- 外层循环遍历子数组的起始位置
i。 - 内层循环遍历从起始位置
i到数组末尾的所有子数组的结束位置j。 - 对于每个子数组,我们使用集合
seen来记录不同的元素,集合的大小即为当前子数组的权值。每次遇到一个新的子数组时,更新对应的计数器。
- 外层循环遍历子数组的起始位置
-
返回结果:
- 最终返回数组
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])
解题思路详解
-
初始化:
- 我们首先创建一个
c数组,用来记录每个权值的子数组数量。c[i]存储的是权值为i的子数组的数量。注意,数组的大小是n+1,因为权值从 1 到n,所以我们需要包括n这个位置。
- 我们首先创建一个
-
双重循环:
- 外层循环
i遍历所有可能的子数组的起始位置。 - 内层循环
j从位置i开始,遍历到数组的末尾。对于每一个子数组,我们用seen集合记录当前子数组中出现的不同元素。 - 每遇到一个新的元素,就把它加入
seen集合。此时,len(seen)即为当前子数组的权值,我们更新计数器c[len(seen)]。
- 外层循环
-
返回结果:
- 最后,返回
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, 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 的子数组没有。
- 权值为 1 的子数组有 5 个:
-
-
示例 2:
-
输入:
[1, 1, 1] -
输出:
[6, 0, 0] -
解释:
- 权值为 1 的子数组有 6 个:
[1],[1],[1],[1, 1],[1, 1],[1, 1, 1] - 权值为 2 和 3 的子数组没有。
- 权值为 1 的子数组有 6 个:
-
总结
通过枚举所有连续子数组并使用集合来记录不同元素,我们能够在 O(n^2) 的时间复杂度内完成这个问题的求解。此方法直观且易于理解,适合处理较小规模的输入。如果问题规模更大,可能需要进一步优化。