Leetcode 992:K 个不同整数的子数组(超详细的解法!!!)

给定一个正整数数组 A,如果 A 的某个子数组中不同整数的个数恰好为 K,则称 A 的这个连续、不一定独立的子数组为好子数组

(例如,[1,2,3,1,2] 中有 3 个不同的整数:12,以及 3。)

返回 A好子数组的数目。

示例 1:

输出:A = [1,2,1,2,3], K = 2
输入:7
解释:恰好由 2 个不同整数组成的子数组:[1,2], [2,1], [1,2], [2,3], [1,2,1], [2,1,2], [1,2,1,2].

示例 2:

输入:A = [1,2,1,3,4], K = 3
输出:3
解释:恰好由 3 个不同整数组成的子数组:[1,2,1,3], [2,1,3], [1,3,4].

提示:

  1. 1 <= A.length <= 20000
  2. 1 <= A[i] <= A.length
  3. 1 <= K <= A.length

解题思路

首先我们看到数据量是20000,所以很明显就不能使用暴力破解了。问题一拿到手,不难想到通过滑动窗口处理。

我们定义[i,j)这个区间内的元素表示最少的构成K个不同整数的子区间,而[i,k)表示最少的构成K+1个不同整数的子区间,那么此时以A[i]为起始的k个不同整数组成的子数组个数有k-j+1个。那么现在我们的问题就变成了,怎么确定我们区间中有K个不同的整数(最短区间长度情况)呢?

我们可以通过一个dict存放我们访问到的元素和其个数,如果dict的数量等于K的时候,那么此时就是我们的j位置。下一步,如何确定K+1不同整数的子区间呢?我们此时只需要判断A[k]在不在dic中,如果不在的话,那么此时k的位置自然就是我们需要的。当我们判断完这样一轮后,我们就可以将A[i]删除了,然后接着判断下一个A[i]开头的区间即可。

class Solution:
    def subarraysWithKDistinct(self, A: 'List[int]', K: 'int') -> 'int':
        n = len(A)
        cnt = collections.defaultdict(int)
        res, j = 0, 0
        
        for i in range(n):
            j = max(j, i)
            while j < n and len(cnt) < K:
                cnt[A[j]] += 1
                j += 1
                
            if len(cnt) < K:
                break
                
            k = j
            while k < n and A[k] in cnt:
                k += 1
                
            res += k - j + 1
            
            cnt[A[i]] -= 1
            if cnt[A[i]] == 0:
                del cnt[A[i]]
            
        return res

上面代码中有两个细节需要注意,我们的j=max(i,j),因为我们的dict中存储了[i, j)的数据。我们这里还有一个剪枝操作,就是len(dic) < k的时候跳出循环。

这个问题还有一个不错的解法。我们可以将这个问题拆解成两个子问题,我们可以先求出最多K个不同整数的子数组有f_m(K)个,最多K-1一个不同整数的子数组有f_m(K-1)个。那么恰好K个不同整数的解f_e(K)就是

  • f e ( K ) = f m ( K ) − f m ( K − 1 ) f_e(K)=f_m(K)-f_m(K-1) fe(K)=fm(K)fm(K1)

为什么要这样做呢?因为最多K个不同整数的子数组这个问题很容易求。那么这个问题怎么求呢?我们同样定义两个指针ij,表示[i,j]区间。通过和之前解法相同的思路,我们建立一个dict去存放访问过的元素,当len(dict)>K的时候,我们就要从i开始向j,删除dict中对应区间[i,j]中的元素,直到len(dict)==K,在这个过程中,我们只需要不断累加区间个数j-i+1即可(因为[i,j]可以拆分为j-i+1个以A[j]结尾的区间)。

class Solution:
    def subarraysWithKDistinct(self, A: 'List[int]', K: 'int') -> 'int':
        return self.atMostK(A, K) - self.atMostK(A, K - 1)

    def atMostK(self, A, K):
        cnt = collections.defaultdict(int)
        res = i = 0
        for j in range(len(A)):
            cnt[A[j]] += 1
            while len(cnt) > K:
                cnt[A[i]] -= 1
                if cnt[A[i]] == 0:
                    del cnt[A[i]]
                i += 1

            res += j - i + 1
        return res

reference:

https://leetcode.com/problems/subarrays-with-k-different-integers/discuss/234482/JavaC%2B%2BPython-Sliding-Window-with-Video

我将该问题的其他语言版本添加到了我的GitHub Leetcode

如有问题,希望大家指出!!!

你可能感兴趣的:(Problems,leetcode解题指南)