news 2026/9/24 0:19:48

双指针专题(十):恰好等于的困境——「K 个不同整数的子数组」

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
双指针专题(十):恰好等于的困境——「K 个不同整数的子数组」

场景想象:你是一个统计员,要把数组切成很多段。 老板要求:每一段里必须恰好包含K种不同的数字。

  • 比如[1, 2, 1, 2, 3],K=2

  • [1, 2, 1, 2]是符合的(只有 1 和 2 两种)。

  • [1, 2, 1, 2, 3]不符合(有 3 种)。

  • [1]不符合(只有 1 种)。

难点:滑动窗口最擅长处理的是“最多 K 个”(类似《水果成篮》)或者“至少 K 个”。 对于“恰好 K 个”,窗口的左边界很难确定。因为“恰好 2 个”的情况可能有很多种(比如[1, 2][1, 2, 1]都是),左指针到底缩到哪里才算完呢?

力扣 992. K 个不同整数的子数组

https://leetcode.cn/problems/subarrays-with-k-different-integers/

题目分析:

  • 输入:数组nums,整数k

  • 输出:满足条件的子数组数量。

核心思维:恰好(K) = 最多(K) - 最多(K-1)

这是一个非常经典的集合论思想:

  • 最多 K 种:包含“恰好 1 种”、“恰好 2 种” ... “恰好 K 种”。

  • 最多 K-1 种:包含“恰好 1 种” ... “恰好 K-1 种”。

如果你把这两个集合相减,剩下的不就是“恰好 K 种”了吗?

转化优势:“最多包含 K 种整数的子数组数量”非常简单,就是标准的滑动窗口(和《水果成篮》一模一样)。 我们只需要写一个 helper 函数atMost(k),然后调用两次即可:return atMost(k) - atMost(k - 1);

atMost(k)的计数逻辑:当窗口[left, right]满足“最多 K 种”时,以nums[right]结尾的、满足条件的子数组有多少个? 答案是right - left + 1个。

  • 比如[1, 2](K=2)。以 2 结尾的子数组有[2][1, 2],共 2 个。

  • 这个公式累加起来,就是总数。

代码实现 (JavaScript)

JavaScript

/** * @param {number[]} nums * @param {number} k * @return {number} */ var subarraysWithKDistinct = function(nums, k) { // 核心公式:恰好 K = 最多 K - 最多 K-1 return atMost(nums, k) - atMost(nums, k - 1); }; /** * 辅助函数:求最多包含 k 种不同整数的子数组数量 * 这就是标准的滑动窗口模板(类似水果成篮) */ function atMost(nums, k) { let left = 0; let right = 0; let count = 0; // 记录符合条件的子数组总数 let distinctCount = 0; // 当前窗口有多少种不同的数 // 使用数组代替 Map 统计频率,性能会好很多(题目提示 nums[i] <= 20000) // 如果没有范围限制,可以用 Map const freq = new Array(nums.length + 1).fill(0); while (right < nums.length) { // --- 进窗口 --- if (freq[nums[right]] === 0) { distinctCount++; } freq[nums[right]]++; right++; // --- 出窗口 --- // 如果种类超过 k,必须收缩 while (distinctCount > k) { freq[nums[left]]--; if (freq[nums[left]] === 0) { distinctCount--; } left++; } // --- 核心累加 --- // 此时窗口 [left, right-1] 内的种类 <= k // 那么以 right-1 结尾的子数组数量就是窗口长度 count += right - left; } return count; }

深度模拟

假设nums = [1, 2, 1, 2, 3],k = 2

  1. 计算atMost(2):

    • [1]-> +1

    • [1, 2]-> +2 (子数组:2,1,2)

    • [1, 2, 1]-> +3 (子数组:1,2,1,1,2,1)

    • [1, 2, 1, 2]-> +4

    • 遇到 3 (种类变3) -> 缩左边直到[2, 3]-> +2

    • 总数 A

  2. 计算atMost(1):

    • [1]-> +1

    • 遇到 2 (种类变2) -> 缩左边直到[2]-> +1

    • 遇到 1 (种类变2) -> 缩左边直到[1]-> +1

    • ...

    • 总数 B

  3. 结果A - B就是我们要的答案。

总结

恭喜你!🎉 到这里,我们的双指针与滑动窗口专题就彻底结业了!

我们从最简单的快慢指针(移除元素)开始,一路打怪升级,经过了对撞指针(三数之和)、不定长窗口(最小覆盖子串),最后攻克了单调队列(滑动窗口最大值)和数学转换(K个不同整数)。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/20 14:59:04

清华镜像站rsync命令同步HunyuanOCR模型数据集

清华镜像站rsync命令同步HunyuanOCR模型数据集 在AI研发一线工作的人都深有体会&#xff1a;一个项目启动阶段最耗时的&#xff0c;往往不是写代码、调模型&#xff0c;而是“等下载”——尤其是面对动辄十几甚至上百GB的大模型权重文件。当你兴致勃勃地准备复现一篇论文或部署…

作者头像 李华
网站建设 2026/9/20 14:56:22

【资深架构师亲述】:我为何在高并发项目中放弃C++改用Rust(附性能对比图)

第一章&#xff1a;C在高并发系统中的历史地位与挑战C 自诞生以来&#xff0c;一直是构建高性能、低延迟系统的首选语言之一。其对底层硬件的直接控制能力、零成本抽象特性以及丰富的模板机制&#xff0c;使其在金融交易系统、实时通信平台和大型互联网后端服务中占据核心地位。…

作者头像 李华
网站建设 2026/9/20 15:09:46

C++高效加载大语言模型的4种方案对比,第3种竟节省50%资源

第一章&#xff1a;C AIGC 模型加载技术概述在人工智能生成内容&#xff08;AIGC&#xff09;领域&#xff0c;C凭借其高性能与底层控制能力&#xff0c;成为部署大规模模型的重要工具。模型加载作为推理流程的起点&#xff0c;直接影响系统的启动速度、内存占用和运行效率。现…

作者头像 李华
网站建设 2026/9/20 14:55:26

C#调用HunyuanOCR接口示例代码分享(基于HttpClient)

C# 调用 HunyuanOCR 接口实战&#xff1a;轻量大模型与企业应用的高效集成 在银行柜台&#xff0c;一名柜员将一张身份证放在扫描仪上&#xff0c;不到三秒&#xff0c;姓名、性别、身份证号等信息已自动填入业务系统&#xff1b;在医院档案室&#xff0c;上千份手写病历正被高…

作者头像 李华
网站建设 2026/9/19 19:11:14

Dify可视化编排调用HunyuanOCR API实现合同识别机器人

Dify可视化编排调用HunyuanOCR API实现合同识别机器人 在企业日常运营中&#xff0c;每天都有成百上千份合同、发票、证件等待处理。传统方式依赖人工逐字录入&#xff0c;效率低、易出错&#xff0c;尤其当文档格式多样、语言混杂时&#xff0c;更是苦不堪言。有没有一种方法&…

作者头像 李华
网站建设 2026/9/20 17:46:52

计算机毕业设计springboot玩具公司进销存管理系统 计算机毕业设计springboot玩具公司进销存管理系统 SpringBoot框架下的玩具公司库存、采购及销售一体化管理系统

计算机毕业设计springboot玩具公司进销存管理系统4bas39 &#xff08;配套有源码 程序 mysql数据库 论文&#xff09; 本套源码可以在文本联xi,先看具体系统功能演示视频领取&#xff0c;可分享源码参考。随着信息技术的飞速发展&#xff0c;传统玩具公司的进销存管理方式面临着…

作者头像 李华