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

57 阅读6分钟

148.小A的子数组权值

问题描述

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

思路解析

  • 整体思路方向
    要解决的问题是统计给定长度为 n 的数组 a 中,权值(连续子数组内不同元素的个数)分别为 1 到 n 的子数组数量。整体思路是通过两层循环遍历出所有可能的连续子数组,在遍历每个子数组的过程中,实时统计其中不同元素的个数(也就是权值),并相应地更新对应权值的子数组数量统计结果。

  • 各环节思路

    • 1.初始化统计列表环节
      创建一个长度为 n + 1 的列表 c,并初始化为全 0。这个列表的作用是用于记录权值从 0 到 n 的子数组数量,索引对应权值,后续在遍历子数组过程中,根据计算出的子数组权值来更新对应索引位置上的值,不过最后返回结果时只需要返回索引 1 到 n 对应的部分(因为权值为 0 的情况不符合题意要求)。
    • 2.子数组遍历与元素统计环节
      通过两层嵌套的 for 循环来生成所有可能的连续子数组。外层循环以变量 i 控制子数组的起始位置,从 0 到 n - 1 进行遍历;内层循环以变量 j 控制子数组的结束位置,从外层循环确定的起始位置 i 开始,一直到 n - 1 进行遍历,这样 a[i:j + 1] 就能表示出每一个连续子数组了。
      在遍历每个子数组(也就是内层循环每次执行时)的过程中,使用一个字典 count_dict 来记录当前子数组中每个元素出现的次数。每当遇到一个元素 a[j],先判断它是否已经在字典 count_dict 中,如果在,就将其对应出现次数加 1;如果不在,说明是新出现的不同元素,将其加入字典且出现次数记为 1,同时把记录不同元素个数的变量 distinct_count 加 1,以此实时统计出当前子数组内不同元素的个数(即权值)。
    • 3.权值对应数量更新环节
      在计算出当前子数组的权值 distinct_count 后,判断如果它小于等于 n,就将统计列表 c 中索引为 distinct_count 的元素值加 1,表示权值为 distinct_count 的子数组数量又多了一个。
    • 4.返回结果环节
      最后返回统计列表 c 中索引从 1 到 n 的切片部分,也就是只返回符合题意要求的权值为 1 到 n 的子数组数量统计结果。

解题步骤

1.函数定义与初始化统计列表

定义了一个名为 solution 的函数,它接收两个参数,一个是整数 n,表示数组的长度;另一个是列表 a,代表给定的数组。在函数内部,首先创建了一个长度为 n + 1 的列表 c,并使用列表推导式初始化为全 0,这个列表将用于后续统计不同权值的子数组数量,其索引对应权值(例如 c[2] 用于统计权值为 2 的子数组数量)。

2.两层循环遍历子数组并统计元素出现次数与权值

  • 外层循环:
    使用 for 循环,让变量 i 从 0 到 n - 1 遍历,它控制了子数组的起始位置。每次外层循环开始,都会初始化一个空字典 count_dict,用于记录当前从位置 i 开始的子数组中各元素出现次数,同时将记录不同元素个数的变量 distinct_count 初始化为 0。

  • 内层循环:
    在内层的 for 循环中,变量 j 从外层循环确定的起始位置 i 开始,到 n - 1 进行遍历,它确定了子数组的结束位置,这样就能通过切片 a[i:j + 1] 得到每一个连续子数组了。对于当前子数组中的每个元素 a[j]

    • 首先通过 if a[j] in count_dict 判断它是否已经在字典 count_dict 中,如果在,就执行 count_dict[a[j]] += 1,也就是将该元素在当前子数组中的出现次数加 1;如果不在字典中,说明这是一个新出现的不同元素,执行 count_dict[a[j]] = 1 将其加入字典且出现次数记为 1,同时执行 distinct_count += 1,将记录不同元素个数的变量 distinct_count 加 1,这样就能实时统计出当前子数组内不同元素的个数(即权值)了。
    • 然后通过 if distinct_count <= n 判断当前子数组的权值 distinct_count 是否小于等于 n,如果是,就执行 c[distinct_count] += 1,将统计列表 c 中对应索引(也就是权值对应的位置)的元素值加 1,表示权值为 distinct_count 的子数组数量又多了一个。

复杂度分析

  • 时间复杂度: 代码通过两层嵌套循环生成子数组,外层循环执行 n 次,内层循环最多执行 n 次,共O(n^2)次循环操作来生成子数组,后续计算子数组权值及统计操作复杂度相对较低,整体时间复杂度由循环主导,为O(n^2)。

  • 空间复杂度: 定义了长度为 n + 1 的 result 列表用于统计,空间复杂度是O(n),循环中临时变量及临时集合占用空间相对固定,可忽略不计,所以整体空间复杂度为O(n)。

Code

def solution(n: int, a: list) -> list:
    c = [0 for _ in range(n + 1)]
    
    # 遍历所有可能的子数组
    for i in range(n):
        # 使用字典记录当前子数组中每个元素的出现次数
        count_dict = {}
        distinct_count = 0
        
        for j in range(i, n):
            # 更新当前元素的出现次数
            if a[j] in count_dict:
                count_dict[a[j]] += 1
            else:
                count_dict[a[j]] = 1
                distinct_count += 1
            
            # 更新权值为distinct_count的子数组数量
            if distinct_count <= n:
                c[distinct_count] += 1
    
    return c[1:]

if __name__ == '__main__':
    print(solution(4, [1, 2, 2, 3]) == [5, 4, 1, 0])
    print(solution(3, [1, 1, 1]) == [6, 0, 0])
    print(solution(5, [1, 2, 3, 2, 1]) == [5, 5, 5, 0, 0])