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

91 阅读4分钟

题目:小A拿到了一个长度为n的数组,并且定义了一个连续子数组的“权值”为该子数组内不同元素的个数。现在,她想知道,权值分别为1,2,3,…,n的子数组数量有多少个。 你需要根据给定的数组,输出一个包含n个整数的数组,第i个数表示权值为i的子数组数量。

  • 解题思路:
  1. 我们可以使用双指针(滑动窗口)的方法来解决这个问题。通过移动左右指针来不断扩展和收缩子数组,同时统计不同元素的个数,也就是子数组的“权值”。
  2. 首先,初始化两个指针  left  和  right  都指向数组的开头,然后逐步移动  right  指针来扩大子数组的范围。
  3. 在移动  right  指针的过程中,我们使用一个字典(或者集合)来记录已经出现过的元素,以便快速判断子数组内不同元素的个数。
  4. 当子数组的权值达到我们想要统计的某个值  i  时,记录下此时满足权值为  i  的子数组数量。然后通过移动  left  指针来收缩子数组,继续寻找下一个满足条件的子数组,直到  right  指针到达数组末尾。
  • 示例图示(以简单数组为例):

假设我们有数组  [1, 2, 1, 3] ,我们来演示一下整个过程。

  • 初始时, left = 0 , right = 0 ,字典  count_dict  为空。
  • 当  right = 0  时,子数组为  [1] ,权值为  1 ,记录下权值为  1  的子数组数量加  1 。
  • 移动  right  指针,当  right = 1  时,子数组为  [1, 2] ,权值为  2 ,记录下权值为  2  的子数组数量加  1 。
  • 继续移动  right  指针,当  right = 2  时,子数组为  [1, 2, 1] ,此时字典中记录的元素为  {1: 2, 2: 1} ,权值仍然为  2 ,继续移动  right  指针。
  • 当  right = 3  时,子数组为  [1, 2, 1, 3] ,字典中记录的元素为  {1: 2, 2: 1, 3: 1} ,权值为  3 ,记录下权值为  3  的子数组数量加  1 。

然后通过收缩  left  指针,再次寻找满足不同权值的子数组,比如当  left = 1 , right = 3  时,子数组为  [2, 1, 3] ,权值为  3 ,又可以记录下权值为  3  的子数组数量加  1 ,以此类推,直到遍历完整个数组。

Python代码如下:

from typing import List def count_subarrays(nums: List[int]) -> List[int]: n = len(nums) result = [0] * n for left in range(n): count_dict = {} right = left while right < n: if nums[right] in count_dict: count_dict[nums[right]] += 1 else: count_dict[nums[right]] = 1 weight = len(count_dict) result[weight - 1] += 1 right += 1 return result  

代码详解:

  1. count_subarrays  函数接受一个整数列表  nums  作为参数,并返回一个长度为  n  的列表  result ,其中  n  是输入数组  nums  的长度。这个返回的列表  result  就是我们要统计的权值分别为  1, 2, 3, …, n  的子数组数量。
  2. 首先,我们初始化  result  为一个长度为  n  的全零列表,用于存储每个权值对应的子数组数量。
  3. 然后,我们使用一个外层循环,通过遍历  left  指针从  0  到  n - 1 ,来考虑以每个元素作为子数组起始位置的情况。
  4. 对于每个  left  的值,我们在内层循环中初始化一个空字典  count_dict ,用于记录子数组中出现的元素及其出现次数。同时,我们将  right  指针也初始化为  left ,表示从当前  left  位置开始的子数组。
  5. 在内部循环中,我们不断移动  right  指针来扩大子数组。每次移动  right  指针时,我们检查当前元素  nums[right]  是否已经在  count_dict  中。如果在,就将其对应的计数加  1 ;如果不在,就将其添加到  count_dict  中,并将计数设为  1
  6. 然后,我们通过计算  count_dict  的长度来得到当前子数组的权值  weight 。由于列表索引是从  0  开始的,所以我们将  weight - 1  作为索引,将  result  中对应的元素加  1 ,表示找到了一个权值为  weight  的子数组。
  7. 最后,当  right  指针遍历完整个数组后,我们就得到了所有以当前  left  位置开始的子数组的权值情况。外层循环继续移动  left  指针,重复上述过程,直到考虑完所有可能的子数组起始位置。
  8. 最终,函数返回  result  列表,其中第  i  个元素就表示权值为  i + 1  的子数组数量。