unordered_set,滑动窗口算法

目录

unordered_set

滑动窗口算法

1.滑动窗口的概念

2.滑动窗口算法举例

3.实例

4.总结

unordered_set
C++ 11 为 STL 标准库增添了 4 种无序(哈希)容器 ,unordered_set 容器就是其一
 unordered_set 容器,可直译为“无序 set 容器”,即 unordered_set 容器和 set 容器很像,
 唯一的区别就在于 set 容器会自行对存储的数据进行排序,而 unordered_set 容器不会。
 实现 unordered_set 容器的模板类定义在<unordered_set>头文件,并位于 std 命名空间中。
 这意味着,如果程序中需要使用该类型容器,则首先应该包含如下代码:
 #include <unordered_set>
 using namespace std;


一、滑动窗口的概念
顾名思义,就像一个滑动的窗口,套在一个序列中,左右的滑动,窗口内就是一个内容集。
二、滑动窗口算法举例
LeetCode_209长度最小的子数组(https ://leetcode-cn.com/problems/minimum-size-subarray-sum/)
给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的连续子数组。
如果不存在符合条件的连续子数组,返回 0。
示例 :
输入: s = 7, nums = [2, 3, 1, 2, 4, 3]
输出 : 2
解释 : 子数组[4, 3] 是该条件下的长度最小的连续子数组。
思路:
滑动窗口,就是不断的调节子序列的起始位置和终止位置,从而得出我们要想的结果。
在本题中实现滑动窗口,主要确定如下三点:
窗口内是什么?
如何移动窗口的起始位置?
如何移动窗口的结束位置?
窗口就是 满足其和 ≥ s 的长度最小的 连续 子数组。
窗口的起始位置如何移动:如果当前窗口的值大于s了,窗口就要向前移动了(也就是该缩小了)。
窗口的结束位置如何移动:窗口的结束位置就是遍历数组的指针,窗口的起始位置设置为数组的起始位置就可以了。

#include<iostream>
using namespace std;
const int maxn = 1e5 + 10;
int n, m;
int num[maxn];
int main()
{
    ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    cin >> n >> m;
    for (int i = 0; i < n; i++) {
        cin >> num[i];
    }
    int result = INT32_MAX;  // 初始化子序列的最小长度为无穷大
    int sum = 0;    //滑动窗口数值之和
    int i = 0;  //滑动窗口起始位置
    int len = 0;  //滑动窗口的长度
    for (int j = 0; j < n; j++) {  //滑动窗口终止位置
        sum += num[j];
        while (sum >= m) {
            len = j - i + 1;
            result = result < len ? result : len;
            sum -= num[i++]; // 这里体现出滑动窗口的精髓之处,不断变更i(子序列的起始位置)
        }
    }
    cout<<(result == INT32_MAX ? 0 : result)<<'\n';
    return 0;
}


三.实例:
1.2022 第十三届蓝桥杯省赛 C / C++ B组
试题F: 统计子矩阵
时间限制 : 1.0s 内存限制 : 256.0MB 本题总分 : 15分
【问题描述】
给定一个N×M的矩阵A,请你统计有多少个子矩阵(最小1×1,最大NxM)满足子矩阵中所有数的和不超过给定的整数K ?
【输入格式】
第一行包含三个整数N, M和K.
之后N行每行包含M个整数,代表矩阵A.
【输出格式】
一个整数代表答案。
【样例输入】
3 4 10
1 2 3 4
5 6 7 8
9 10 11 12
【样例输出】
19
【样例说明】
满足条件的子矩阵一共有19,包含 :
大小为1×1的有10个。
大小为1×2的有3个。
大小为1×3的有2个。
大小为1×4的有1个。
大小为2×1的有3个。
【评测用例规模与约定】
对于30 % 的数据, N, M ≤ 20.
对于70 % 的数据,N, M ≤ 100.
对于100 % 的数据,1 ≤ N, M ≤ 500; 0 ≤ Aij ≤1000; 1 ≤ K ≤ 250000000.
思路:使用前缀和+滑动窗口(适用于解决矩阵)
使用纵向前缀和+二重循环枚举上下边界+滑动窗口左右边界实现

#include<iostream>
using namespace std;
typedef long long ll;
const int maxn = 5e2 + 10;
int n, m,k;
ll a[maxn][maxn];
ll ans;
int main()
{
    ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    cin >> n >> m >> k;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            cin >> a[i][j];
            //维护****纵向前缀和****
            a[i][j] += a[i - 1][j];
        }
    }
    for (int i = 1; i <= n; i++) {   //遍历上边界
        for (int j = i; j <= n; j++) {  //遍历下边界
            int l = 1, r = 1;
            int sum = 0;
            for (r = 1; r <= m; r++) {
                sum += a[j][r] - a[i - 1][r]; // 加上右端点处的和
                while (sum > k) {
                    sum -= a[j][l] - a[i - 1][l]; //减去移出去的左端点处的和
                    l++;
                }
                ans += r - l + 1; // 方法数就是找到的区间大小累加
            }
        }
    }
    cout << ans << '\n';
    return 0;
}


2.LeetCode_3:无重复字符的最长子串
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: "abcabcbb"
输出 : 3
解释 : 因为无重复字符的最长子串是 "abc",所以其长度为 3。
示例 2 :
输入 : "bbbbb"
输出 : 1
解释 : 因为无重复字符的最长子串是 "b",所以其长度为 1。
示例 3 :
输入 : "pwwkew"
输出 : 3
解释 : 因为无重复字符的最长子串是 "wke",所以其长度为 3。
请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
思路:利用滑动窗口+数据结构unordered_set的特性erase(特定位置)insert+count

#include<iostream>
#include<cstring>
#include<unordered_set>
using namespace std;
unordered_set<char>occ;
string s;
int main()
{
    ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    cin >> s;
    int n = s.size();
    int r = -1, ans = 0;  // 右指针,初始值为 -1,相当于我们在字符串的左边界的左侧,还没有开始移动
    for (int i = 0; i < n; i++) {
        if (i != 0)
            occ.erase(s[i - 1]);    //左指针向右移动一格,移除一个字符
        while (r < n - 1 && !occ.count(s[r + 1])) {
            occ.insert(s[r + 1]);
            r++;
        }
        ans = max(ans, r - i + 1);
    }
    cout << ans << '\n';
    return 0;
}


3.LeetCode_1004:最大连续1的个数 III
给定一个由若干 0 和 1 组成的数组 A,我们最多可以将 K 个值从 0 变成 1 。
返回仅包含 1 的最长(连续)子数组的长度。
示例 1:
输入:A = [1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0], K = 2
输出:6
解释:
[1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1]
粗体数字从 0 翻转到 1,最长的子数组长度为 6。
示例 2:
输入:A = [0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1], K = 3
输出:10
解释:
[0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1]
粗体数字从 0 翻转到 1,最长的子数组长度为 10。
提示:
1 <= A.length <= 20000
0 <= K <= A.length
A[i] 为 0 或 1

#include<iostream>
using namespace std;
const int maxn = 2e4 + 10;
int n, k;
int nums[maxn];
int main()
{
    ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    cin >> n >> k;
    for (int i = 0; i <n; i++) {
        cin >> nums[i];
    }
    int Count = 0;
    int l = 0;
    int ans = 0;
    for (int i = 0; i < nums[i]; i++) {    //枚举右边界
        if (!nums[i])
            Count++;
        while (Count>k) {
            if (!nums[l++])
                Count--;
        }
        ans = max(i - l + 1, ans);
    }
    cout << ans << '\n';
    return 0;
}


 时间复杂度:O(N),因为每个元素只遍历了一次。
 空间复杂度:O(1),因为使用了常数个空间。


四:总结
滑动数组不断的调节子序列的起始位置和终止位置
其中while的作用是1.通过调节左或右边界使上限条件不再满足,能够继续运行直到遍历完成
2.通过调节左或右边界使之满足上限条件,用另外功能使上限条件不再满足,能够继续运行直到遍历完成
其中for如果调节左边界,则while调节右边界,反之亦然

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
unordered_map与unordered_set有什么区别? 回答: unordered_map和unordered_set都是关联式容器,类似于键值对 (key-value) 的模型。它们的底层实现方式不同,unordered_map使用哈希表作为底层数据结构,而unordered_set也是使用哈希表。unordered_map和unordered_set的区别在于它们存储的类型不同,即unordered_map存储键值对,而unordered_set存储单个元素。此外,unordered_map和unordered_set在功能上也有一些区别。unordered_map提供了以键为索引的查找功能,而unordered_set则提供了判断元素是否存在的功能。从效率上来看,unordered_map和unordered_set的增删查改操作的时间复杂度都是O(1),即常数时间。而map和set的时间复杂度为O(logN),其中N是容器中的元素数量。所以在对效率要求较高的情况下,选择unordered_map和unordered_set会更合适。但是,unordered_map和unordered_set相比于map和set会消耗更多的内存空间。因此,在对数据有排序或者对空间有要求的情况下,选择map和set;而对于对效率有要求的情况,选择unordered_map和unordered_set更合适。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [STL详解(十二)—— unordered_set、unordered_map的介绍及使用](https://blog.csdn.net/chenlong_cxy/article/details/122277348)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [模拟实现unordered_map和unordered_set超详解(C++)](https://blog.csdn.net/m0_67430750/article/details/124760725)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [unordered_set和unordered_map的使用【STL】](https://blog.csdn.net/m0_63312733/article/details/128000844)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值