【滑动窗口】C++算法:K 个不同整数的子数组

简介: 【滑动窗口】C++算法:K 个不同整数的子数组

LeetCoe992 K 个不同整数的子数组

给定一个正整数数组 nums和一个整数 k,返回 nums 中 「好子数组」 的数目。

如果 nums 的某个子数组中不同整数的个数恰好为 k,则称 nums 的这个连续、不一定不同的子数组为 「好子数组 」。

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

子数组 是数组的 连续 部分。

示例 1:

输入:nums = [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:

输入:nums = [1,2,1,3,4], k = 3

输出:3

解释:恰好由 3 个不同整数组成的子数组:[1,2,1,3], [2,1,3], [1,3,4].

提示:

1 <= nums.length <= 2 * 104

1 <= nums[i], k <= nums.length

滑动窗口

复杂度: O(n)。

枚举子数组的左边界left,时间复杂度O(n);枚举r1,r2时间复杂度O(n)。由于r1和r2没有复位,所以总时间复杂度是O(n)。

r1 nums[left,r1)有k个不同的整数,如果有多个符合的r1,取最小值。如果没有符合的r1,则r1为m_c
r2 nums[left,r2)有k+1个不同的整数,如果有多个符合的r2,取最小值。如果没有符合的r2,则r2为m_c

如果没有符合的r1,则忽略或直接退出。

如果r2合法 则nums[left,r)刚好有k个数,r取值范围[r1,r2),数量为r2-r1
如果r2非法则 nums[left,r)刚好有k个数,r取值范围[r1,m_c],数量r2+1-r1

代码

核心代码

template<class KEY>
class CKeyCount
{
public:
  void Add(const KEY& key, int iCount)
  {
    Cnt[key] += iCount;
    if (0 == Cnt[key])
    {
      Cnt.erase(key);
    }
  }
  std::unordered_map<KEY, int> Cnt;
};
class Solution {
public:
  int subarraysWithKDistinct(vector<int>& nums, int k) {
    m_c = nums.size();
    CKeyCount<int> cnt1,cnt2;
    int r1 = 0, r2 = 0;
    int iRet = 0;
    for (int left = 0; left < nums.size(); left++)
    {
      while ((r1 < m_c)&&( cnt1.Cnt.size() < k ) )
      {
        cnt1.Add(nums[r1++],1);
      }
      while ((r2 < m_c) && (cnt2.Cnt.size() < k+1))
      {
        cnt2.Add(nums[r2++], 1);
      }
      if (cnt1.Cnt.size() < k )
      {
        break;      
      }
            iRet += r2 - r1 + (cnt2.Cnt.size()==k); 
      cnt1.Add(nums[left], -1);
      cnt2.Add(nums[left], -1);
    }
    return iRet;
  }
  int m_c;
};

2023年5月版

class Solution {
public:
int subarraysWithKDistinct(vector& nums, int k) {
m_c = nums.size();
if (1 == k)
{
return DoK1(nums);
}
std::unordered_map mValueNum;
std::unordered_map mIndexs;
int left = 0, right = 0;
//[i,vR1[i])表示以索引i开头符合条件的最短子串,[i,vR2[i]]表示以索引i开头符合条件的最长子串
//-1表示没有符合条件的子串
vector vR1(m_c,-1), vR2(m_c,-2);
while ((right < m_c) && (mValueNum.size() < k ))
{
mValueNum[nums[right]]++;
mIndexs[nums[right]].emplace(right);
right++;
}
if (mValueNum.size() != k)
{
return 0;
}
vR1[0] = right;
for (int left = 0; left < m_c; left++)
{
//[left,right) 全部符合条件
while ((right < m_c) && (mValueNum.count(nums[right])))
{
const int iRightValue = nums[right];
mValueNum[iRightValue]++;
mIndexs[iRightValue].emplace(right);
right++;
}
vR2[left] = right;
//删除索引为left的元素
const int iValueLeft = nums[left];
mIndexs[iValueLeft].pop();
if (1 == mValueNum[iValueLeft])
{
mValueNum.erase(iValueLeft);
while ((right < m_c) && (mValueNum.size() < k))
{
const int iRightValue = nums[right];
mValueNum[iRightValue]++;
mIndexs[iRightValue].emplace(right);
right++;
}
if (mValueNum.size() == k)
{
vR1[left + 1] = right;
}
else
{
break;
}
}
else
{
vR1[left + 1] = max(vR1[left], mIndexs[iValueLeft].front()+1);
mValueNum[iValueLeft]–;
}
}
int iRet = 0;
for (int i = 0; i < m_c; i++)
{
iRet += vR2[i] - vR1[i]+1;
}
return iRet;
}
int DoK1(const vector& nums)
{
int iRet = 0;
int iNum = 1;
int iPre = nums[0];
for (int i = 1; i < m_c; i++)
{
if (nums[i] == iPre)
{
iNum++;
}
else
{
iRet += iNum*(iNum + 1) / 2;
iNum = 1;
iPre = nums[i];
}
}
iRet += iNum*(iNum + 1) / 2;
return iRet;
}
int m_c;
};


扩展阅读

视频课程

有效学习:明确的目标 及时的反馈 拉伸区(难度合适),可以先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。

https://edu.csdn.net/course/detail/38771

如何你想快

速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程

https://edu.csdn.net/lecturer/6176

相关下载

想高屋建瓴的学习算法,请下载《喜缺全书算法册》doc版

https://download.csdn.net/download/he_zhidan/88348653

测试环境

操作系统:win7 开发环境: VS2019 C++17

或者 操作系统:win10 开发环境: VS2022 C++17

如无特殊说明,本算法C++ 实现。

相关文章
|
1月前
|
存储 算法
算法入门:专题二---滑动窗口(长度最小的子数组)类型题目攻克!
给定一个正整数数组和目标值target,找出总和大于等于target的最短连续子数组长度。利用滑动窗口(双指针)优化,维护窗口内元素和,通过单调性避免重复枚举,时间复杂度O(n)。当窗口和满足条件时收缩左边界,更新最小长度,最终返回结果。
|
6月前
|
存储 监控 算法
基于 C++ 哈希表算法实现局域网监控电脑屏幕的数据加速机制研究
企业网络安全与办公管理需求日益复杂的学术语境下,局域网监控电脑屏幕作为保障信息安全、规范员工操作的重要手段,已然成为网络安全领域的关键研究对象。其作用类似网络空间中的 “电子眼”,实时捕获每台电脑屏幕上的操作动态。然而,面对海量监控数据,实现高效数据存储与快速检索,已成为提升监控系统性能的核心挑战。本文聚焦于 C++ 语言中的哈希表算法,深入探究其如何成为局域网监控电脑屏幕数据处理的 “加速引擎”,并通过详尽的代码示例,展现其强大功能与应用价值。
158 2
|
7月前
|
机器学习/深度学习 监控 算法
员工上网行为监控软件中基于滑动窗口的C#流量统计算法解析​
在数字化办公环境中,员工上网行为监控软件需要高效处理海量网络请求数据,同时实时识别异常行为(如高频访问非工作网站)。传统的时间序列统计方法因计算复杂度过高,难以满足低延迟需求。本文将介绍一种基于滑动窗口的C#统计算法,通过动态时间窗口管理,实现高效的行为模式分析与流量计数。
197 2
|
4月前
|
存储 监控 算法
基于跳表数据结构的企业局域网监控异常连接实时检测 C++ 算法研究
跳表(Skip List)是一种基于概率的数据结构,适用于企业局域网监控中海量连接记录的高效处理。其通过多层索引机制实现快速查找、插入和删除操作,时间复杂度为 $O(\log n)$,优于链表和平衡树。跳表在异常连接识别、黑名单管理和历史记录溯源等场景中表现出色,具备实现简单、支持范围查询等优势,是企业网络监控中动态数据管理的理想选择。
148 0
|
6月前
|
监控 算法 数据处理
基于 C++ 的 KD 树算法在监控局域网屏幕中的理论剖析与工程实践研究
本文探讨了KD树在局域网屏幕监控中的应用,通过C++实现其构建与查询功能,显著提升多维数据处理效率。KD树作为一种二叉空间划分结构,适用于屏幕图像特征匹配、异常画面检测及数据压缩传输优化等场景。相比传统方法,基于KD树的方案检索效率提升2-3个数量级,但高维数据退化和动态更新等问题仍需进一步研究。未来可通过融合其他数据结构、引入深度学习及开发增量式更新算法等方式优化性能。
181 17
|
5月前
|
机器学习/深度学习 存储 算法
基于 C++ 布隆过滤器算法的局域网上网行为控制:URL 访问过滤的高效实现研究
本文探讨了一种基于布隆过滤器的局域网上网行为控制方法,旨在解决传统黑白名单机制在处理海量URL数据时存储与查询效率低的问题。通过C++实现URL访问过滤功能,实验表明该方法可将内存占用降至传统方案的八分之一,查询速度提升约40%,假阳性率可控。研究为优化企业网络管理提供了新思路,并提出结合机器学习、改进哈希函数及分布式协同等未来优化方向。
160 0
|
5月前
|
存储 机器学习/深度学习 监控
公司电脑上网监控中滑动窗口算法的理论构建与工程实现
本文提出一种基于滑动窗口算法的实时网络流量监控框架,旨在强化企业信息安全防护体系。系统采用分层架构设计,包含数据采集、处理与分析决策三大模块,通过 Java 实现核心功能。利用滑动窗口技术动态分析流量模式,结合阈值检测与机器学习模型识别异常行为。实验表明,该方案在保证高检测准确率的同时支持大规模并发处理,为企业数字化转型提供可靠保障。
143 0
|
7月前
|
存储 机器学习/深度学习 监控
如何监控员工的电脑——基于滑动时间窗口的Java事件聚合算法实现探析​
在企业管理场景中,如何监控员工的电脑操作行为是一个涉及效率与合规性的重要课题。传统方法依赖日志采集或屏幕截图,但数据量庞大且实时性不足。本文提出一种基于滑动时间窗口的事件聚合算法,通过Java语言实现高效、低资源占用的监控逻辑,为如何监控员工的电脑提供一种轻量化解决方案。
196 3
|
9月前
|
编译器 C++ 开发者
【C++篇】深度解析类与对象(下)
在上一篇博客中,我们学习了C++的基础类与对象概念,包括类的定义、对象的使用和构造函数的作用。在这一篇,我们将深入探讨C++类的一些重要特性,如构造函数的高级用法、类型转换、static成员、友元、内部类、匿名对象,以及对象拷贝优化等。这些内容可以帮助你更好地理解和应用面向对象编程的核心理念,提升代码的健壮性、灵活性和可维护性。
|
5月前
|
人工智能 机器人 编译器
c++模板初阶----函数模板与类模板
class 类模板名private://类内成员声明class Apublic:A(T val):a(val){}private:T a;return 0;运行结果:注意:类模板中的成员函数若是放在类外定义时,需要加模板参数列表。return 0;
158 0

热门文章

最新文章