【算法】JS滑动窗口 力扣6293统计好子数组的数目

题目链接

6293. 统计好子数组的数目

题目描述

给你一个整数数组 nums 和一个整数 k ,请你返回 nums 中 好 子数组的数目。

一个子数组 arr 如果有 至少 k 对下标 (i, j) 满足 i < j 且 arr[i] == arr[j] ,那么称它是一个 好 子数组。

子数组 是原数组中一段连续 非空 的元素序列。

题目示例

输入:nums = [3,1,4,3,2,2,4], k = 2

输出:4

解释:总共有 4 个不同的好子数组:

- [3,1,4,3,2,2] 有 2 对。

- [3,1,4,3,2,2,4] 有 3 对。

- [1,4,3,2,2,4] 有 2 对。

- [4,3,2,2,4] 有 2 对。

题解思路

如何快速判断一个数组中有多少对相等

观察可以发现如果数组内有2个重复的数字则有一对,3个则有3对,4个则有6对

个数 推算结果

1 0

2 0 + 1

3 0 + 1 + 2

4 0 + 1 + 2 + 3

5 0 + 1 + 2 + 3 + 4

即公式为 (cnt * (cnt - 1)) / 2

滑动窗口

右指针不断右移,当符合好数组条件时,右指针及其后面的位置都是满足好数组条件的,此时左指针右移,继续判断当前滑动窗口是否符合条件

因为左右指针最多都移动n次,所以虽然有2个while但是时间复杂度为O(n)

题目解答

我最开始的解法如下,但是超时了

/**
 * @param {number[]} nums
 * @param {number} k
 * @return {number}
 */
var countGood = function(nums, k) {
  let left = 0, right = 0, res = 0, len = nums.length
  const cache = new Map() // 统计下标[left, right]窗口内数字的个数

  while (right < len) {
    const numCnt = cache.get(nums[right]) || 0
    cache.set(nums[right], numCnt + 1)
    while (isGood(cache, k) && left < right) {
      // 右指针及其后面的位置都是满足条件的 
      // 即[left, right] [left, right + 1] ... [left, len - 1] 这么多子数组都是满足条件的
      res +=  len - right  
      
      cache.set(nums[left], cache.get(nums[left]) - 1)
      left++
    }
    right++
  }

  return res
};
// 根据cache判断这个数组是否为好数组 时间复杂度取决于cache.size
function isGood(cache, k) {
  const cnt = Array.from(cache).reduce((p, [_, value]) => {
    return p + ((value - 1) + 1) * (value - 1) / 2
  }, 0)
  return cnt >= k
}

下面是优化后的思路

/**
 * @param {number[]} nums
 * @param {number} k
 * @return {number}
 */
var countGood = function(nums, k) {
  let left = 0, right = 0, res = 0, len = nums.length, cnt = 0
  const cache = new Map() // 统计下标[left, right]窗口内数字的个数
  /**
   * 出现个数  推算结果
   * 1         0
   * 2         0 + 1
   * 3         0 + 1 + 2
   * 4         0 + 1 + 2 + 3
   * 5         0 + 1 + 2 + 3 + 4
   */

  while (right < len) {
    const numCnt = cache.get(nums[right]) || 0
    cache.set(nums[right], numCnt + 1)
    // 按照上面的规律 如果之前出现4次 出现5次的时候 +4 既可
    cnt += numCnt
    
    while (cnt >= k) {
      // !!! 右指针及其后面的位置都是满足条件的 
      // 即[left, right] [left, right + 1] ... [left, len - 1] 这么多子数组都是满足条件的
      res +=  len - right  

      const numCnt = cache.get(nums[left])
      cache.set(nums[left], numCnt - 1)

      // 按照上面的规律 如果之前出现4次 出现3次的时候 -3 既可
      cnt -= numCnt - 1
      
      left++
    }
    right++
  }

  return res
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值