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

解答:

滑动窗口+Map记录窗口元素个数,当窗口长度大于K(说明窗口内部含有重复元素)时,需要考虑窗口内的连续子串可能含有满足元素个数等于K的连续子串。

为了避免重复计算子串,采用固定尾部的方法计算,例如"1,2,1,2"的窗口含有两个子串“2,1,2”和“1,2”。
这个方法是我自己写的,有点费时(6500ms),不过通过了,哈哈哈。

在这里插入图片描述

class Solution {
    int num = 0;
    private void sub(int[] A, int K){
        Map<Integer, Integer> map = new HashMap<>();
        for (int a: A){
            map.put(a, map.getOrDefault(a, 0) + 1);
        }
        int index = 0;
        while (map.size() == K){
            num++;
            if (map.get(A[index]) == 1) { // reduce the first element
                map.remove(A[index]);
            } else {
                map.replace(A[index], map.get(A[index]) - 1);
            }
            index++;
        }
    }

    public int subarraysWithKDistinct(int[] A, int K) {
        int start = 0;
        int end = 0;
        Map<Integer, Integer> map = new HashMap<>();
        map.put(A[0], 1);
        while (end < A.length) {
            if (map.size() == K) {
                num++;
                if (end - start >= K) {
                    sub(Arrays.copyOfRange(A,start+1,end+1), K);
                }
                if (++end < A.length ) { // add the last element
                    map.put(A[end], map.getOrDefault(A[end], 0) + 1);
                } 
            } else if (map.size() < K) {
                if (++end < A.length ) { // add the last element
                    map.put(A[end], map.getOrDefault(A[end], 0) + 1);
                } 
            } else if (map.size() > K) {
                if (map.get(A[start]) == 1) { // reduce the first element
                    map.remove(A[start]);
                } else {
                    map.replace(A[start], map.get(A[start]) - 1);
                }
                start++;
            }
        }
        return num;
    }
}

双滑动窗口法(官方给的),跟上面的方法类似。使用另外一个窗口处理窗口中满足条件的连续子串,第一个窗口跟上面方法的窗口完全一样,第二个窗口表示“以第一个窗口的end为结尾的窗口B,而且这个窗口B是第一个不满足K条件的窗口(就是while (map.size() == K)这个退出条件表示的sub函数中的map)”,然后两个窗口的差值就是以当前end为结尾的满足K条件的连续子串数。

在这里插入图片描述

class Solution {
    class Window {
        Map<Integer, Integer> count;
        int nonzero;

        Window() {
            count = new HashMap();
            nonzero = 0;
        }

        void add(int x) {
            count.put(x, count.getOrDefault(x, 0) + 1);
            if (count.get(x) == 1)
                nonzero++;
        }

        void remove(int x) {
            count.put(x, count.get(x) - 1);
            if (count.get(x) == 0)
                nonzero--;
        }

        int different() {
            return nonzero;
        }
    }
    public int subarraysWithKDistinct(int[] A, int K) {
        Window window1 = new Window();
        Window window2 = new Window();
        int ans = 0, left1 = 0, left2 = 0;

        for (int right = 0; right < A.length; ++right) {
            int x = A[right];
            window1.add(x);
            window2.add(x);

            while (window1.different() > K)
                window1.remove(A[left1++]);
            while (window2.different() >= K)
                window2.remove(A[left2++]);

            ans += left2 - left1;
            System.out.println(ans);
        }

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值