6293. 统计好子数组的数目

219 阅读1分钟

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

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

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

算法
方法一:双指针(固定右边界)(不定长滑动窗口)

思路:问题考察存在多少个连续子数组,存在good pair count 大于等于k。连续子数组是一个不定长的窗口,问题可以转化为存在多少个窗口,窗口内的good pair count 大于等于k。

1.如何统计窗口内的good pair数?
用一个map count记录窗口内每个数字出现的次数,用pair记录窗口内的good pair数。窗口的右边界right右移时,good pair cound增加count[right],count[right]++。窗口的左边界left右移时,good pair cound减少count[left],count[right]--。 这样我们就能实时维护窗口内的good pair count

2.那么窗口怎么移动呢?
我们固定右边界right,left右移直到窗口[left + 1,right]不存在满足条件的子数组: 0--->left-------right
此时,符合条件的子数组长度为left + 1,然后我们将right右移。

注意,left不能移动到不满足的left + 1去,因为随着nums[right]可能和nums[right + 1]相等,随着right的右移,left + 1可能变成符合条件的左端点

3.窗口右移(left->left2, right->right2)之后,新增满足条件的子数组数量是多少?
0--->left---(left2)----right---(right2)
1.左边界为[0,left],右边界为为right2的数字均满足条件(因为right时good pair对就满足条件,再增加一下长度同样满足)。 2.左边界为[left+1. left2],右边界为为right2,同样满足(不满足left就不会右移)
所以以right2为右端点的good pair对长度大于等于k的子数组数目就是left2 + 1

套路:
子数组统计问题通常用不定长滑动窗口(双指针解决)

func countGood(nums []int, k int) int64 {
    // 窗口内各个数的数量
    numCount := make(map[int]int)
    ans := int64(0)
    // 窗口内good pair count
    windowPairs := 0
    for left, right := 0, 0; right < len(nums); right ++ {
        windowPairs = windowPairs + numCount[nums[right]]
        numCount[nums[right]] ++ 
        for left < len(nums) &&  windowPairs - numCount[nums[left]] + 1 >= k {
            // left右移,good pair count减少numCount[nums[left]] - 1
            windowPairs = windowPairs - numCount[nums[left]] + 1
            numCount[nums[left]] -- 
            left ++
        }
        if windowPairs >= k {
            ans = ans + int64(left + 1)
        }
    }
    return ans
}   

方法二:双指针 (固定左边界)
子数组nums[i,j]至少有k对good pair,则nums[i,j+1]...nums[i,n]

func countGood(nums []int, k int) int64 {
    numCount := make(map[int]int)
    ans := int64(0)
    windowPairs := 0
    for left, right := 0, 0; right < len(nums); right ++ {
        windowPairs = windowPairs + numCount[nums[right]]
        numCount[nums[right]] ++ 

        // left为满足条件的子字符串的左端点,左端点每变一次,ans增加一次
        for windowPairs >= k {
            ans = ans + int64(len(nums) - right)
            windowPairs = windowPairs - numCount[nums[left]] + 1
            numCount[nums[left]] -- 
            left ++
        }
    }
    return ans
}