加载中...
返回

难题本 | LeetCode992. K 个不同整数的子数组

题目链接:https://leetcode-cn.com/problems/subarrays-with-k-different-integers/

思路是真难想,想出来之后是真简单。

已经是我目前的水平无法搞定的程度了,在此稍作记录。

题目

给定一个正整数数组 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

分析

这个月是滑动窗口月啊,这题也想着滑动窗口,但是没有以前的题目那么直白了。

我们可以看看滑动窗口常用的场景——“最值”问题:

在这道题中,我们要把“恰好”转换成为“最多”。使用最多包含K个不同整数的子区间个数减掉最多包含K - 1个不同整数的子区间个数,正是所要求的恰好包含K个不同整数的子区间个数。

为什么能想到这样的思路呢?无他,唯手熟尔。

如何使用滑动窗口求解最多包含K个不同整数的子区间个数呢?我们使用两个指针leftright来标识一个左闭右闭区间,每当一个窗口满足其内的元素小于等于K个时,这个窗口将会贡献right - left + 1个子数组。当一个窗口内的元素大于K个时,我们移动左指针,使得窗口内的元素再次小于等于K个,然后再考虑它的贡献。

为什么一个窗口能贡献这么多的子数组呢?以及这种方法的正确性在哪里呢?

示例1为例,数组[1,2,1,2,3], K = 2,我们考虑元素个数小于等于2的所有子数组。

left = right = 0时,窗口内不同元素个数小于等于2,贡献了一个子数组。

left = 0, right = 1时,窗口内不同元素个数等于2,贡献了两个子数组。

……

我们特别地考虑left = 0, right = 3的情况,窗口内不同元素的个数还是2,我们看看它到底是不是贡献了3 - 2 + 1 = 4个子数组。

是的,我们从right向左延伸,包括right所指的元素本身在内,一共能找到right - left + 1 = 4个子数组。

注意是从right向左延伸,因为右边界小于right的子数组一定在更早的循环被考虑过了。比如图中的中间子数组[2, 1],在我们的right = 2时已经考虑过它。

因此,能够写出至多包含K个子元素的滑动窗口代码:

int subarrayWithMostKDistinct(vector<int>& A, int K)
{
    int len = A.size();
    if (len == 1)
        return 1;

    int freq[200001] = { 0 };
    int left = 0;
    int right = 0;
    int cnt = 0;
    int res = 0;
    while (right < len)
    {
        freq[A[right]]++;  // 加入
        if (freq[A[right]] == 1)  // 新的元素
        {
            cnt++;
        }
        while (cnt > K)  // 已经不满足条件
        {
            freq[A[left]]--;
            if (freq[A[left]] == 0)
                cnt--;
            left++;
        }
        res += (right - left + 1);
        right++;
    }
    return res;
}

AC代码

class Solution {
public:
    int subarrayWithMostKDistinct(vector<int>& A, int K)
    {
        /* 最多包含K个不同元素的子数组个数
           ` `
        */
        int len = A.size();
        if (len == 1)
            return 1;

        int freq[200001] = { 0 };
        int left = 0;
        int right = 0;
        int cnt = 0;
        int res = 0;
        while (right < len)
        {
            freq[A[right]]++;  // 加入
            if (freq[A[right]] == 1)  // 新的元素
            {
                cnt++;
            }
            while (cnt > K)  // 已经不满足条件
            {
                freq[A[left]]--;
                if (freq[A[left]] == 0)
                    cnt--;
                left++;
            }
            res += (right - left + 1);
            right++;
        }
        return res;
    }

    int subarraysWithKDistinct(vector<int>& A, int K) {
        return subarrayWithMostKDistinct(A, K) 
            - subarrayWithMostKDistinct(A, K - 1);
    }
};

表现效果莫名其妙😏

人一能之,己百之;人十能之,己千之。果能此道矣,虽愚必明,虽柔必强。

参考资料

[1] 力扣(LeetCode).K个不同整数的子数组[EB/OL].2021-02-08

https://leetcode-cn.com/problems/subarrays-with-k-different-integers/solution/k-ge-bu-tong-zheng-shu-de-zi-shu-zu-by-l-ud34/

10 comments
Anonymous
Markdown is supported
@mpv945
mpv945commentedover 2 years ago

添加图片,如果使用外部图床的http链接 。图片无法点击放大,你那边怎么解决的?

@SGS4ever
SGS4evercommentedover 2 years ago

@mpv945
添加图片,如果使用外部图床的http链接 。图片无法点击放大,你那边怎么解决的?

我的博客没有使用图床,所以没办法帮到你~

@Celetherin
Celetherincommentedover 2 years ago

您好,我也是使用的stack主题,我在照着您的方法添加返回顶部按钮时,遇到了按钮虽然出现、也能够点击,但无法实现实际上的返回顶部功能的问题,我没有任何的代码知识,不知道您有没有解决方法?
另外,也是想提醒一下其他需要这篇教程的朋友,最新版的stack主题,添加返回按钮的组件应该在layouts/partials/sidebar/right.html, 在layouts/_default/single.html中添加代码会导致出现两个右边栏。

@jsjcjsjc
jsjcjsjccommentedover 2 years ago

请教一下博主,如何优雅的给stack主题添加广告哈?
我只想在左或者右侧边栏底部,或者每篇文章底部添加一个小小的广告,但是默认似乎的满屏广告哈~~
感谢

@SGS4ever
SGS4evercommentedover 2 years ago
@ClimbingMouse
ClimbingMousecommentedalmost 2 years ago

你好,按照你的方法设置页面载入动画,这个动画不会停止咋办啊

@46fafa
46fafacommentedalmost 2 years ago

博主你好,请问一下主页布局修改哪里的代码如何作用于整个网页,我发现修改后的布局只存在主页和前两篇文章,其他部分还是没修改的样子

@4kohakunushi
4kohakunushicommentedover 1 year ago

你好,关于左侧栏图标高亮我这里存在一些问题想请教你。我取消了原本主页直接抓取post的内容在中间显示的版块,这个部分改成了其他东西,与此同时新增了一个抓取post信息的与links、search等目录并列的一个目录,现在的问题是这些部分虽然都能正常显示,但是对应的抓取post的那个目录无法选中以后高亮,应该修改增加什么才能让它也可以选中后高亮呢?

@SGS4ever
SGS4evercommentedover 1 year ago

首先我只能基于本文使用的Stack版本来尝试解答,因为没看过当前的Stack主题的代码~
我重新翻了下此前写的关于高亮的内容,理论上只要你的post页面的标题在menu配置中即可高亮。如果post页面是你站点的根路径,那应该可以参考我的文章里写的方法,修改下active的触发逻辑~

@4kohakunushi
你好,关于左侧栏图标高亮我这里存在一些问题想请教你。我取消了原本主页直接抓取post的内容在中间显示的版块,这个部分改成了其他东西,与此同时新增了一个抓取post信息的与links、search等目录并列的一个目录,现在的问题是这些部分虽然都能正常显示,但是对应的抓取post的那个目录无法选中以后高亮,应该修改增加什么才能让它也可以选中后高亮呢?

@sansan-cc
sansan-cccommentedabout 1 month ago

感谢博主的建站帖子,有很大的帮助。

有朋自远方来,不亦说乎?