首页 > 其他分享 >滑动窗口

滑动窗口

时间:2024-10-07 16:00:11浏览次数:6  
标签:字符 窗口 nums int res ++ 滑动 counts

滑动窗口

  • 维持左右边界都不回退的一段范围,求解子数组、子串相关问题
  • 求子数组以每个位置开头或者结尾情况下的答案
  • 找范围和答案指标之间的单调性关系

209. 长度最小的子数组

#include <vector>
#include <valarray>

using namespace std;

class Solution {
public:
    // 时间复杂度 O(n)
    int minSubArrayLen(int target, vector<int> &nums) {
        int res = INT_MAX;
        for (int l = 0, r = 0, sum = 0; r < nums.size(); r++) {
            // 查看以 nums[r] 结尾的子数组中是否有符合条件的
            sum += nums[r];
            // 尽量减小以 nums[r] 结尾的子数组长度
            while (sum - nums[l] >= target) {
                sum -= nums[l];
                l++;
            }
            // 记录所有位置结尾中的最短数组
            if (sum >= target) res = min(res, r - l + 1);
        }
        return res == INT_MAX ? 0 : res;
    }
};

3. 无重复字符的最长子串

  • 记录字符上一次出现的位置
#include <vector>
#include <string>

using namespace std;

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int len = s.length();
        int res = 0;
        // 记录每个字符上次出现的位置
        vector<int> last(128, -1);
        for (int l = 0, r = 0; r < len; r++) {
            // 更新窗口左边界
            l = max(l, last[s[r]] + 1);
            // 记录最大窗口长度
            res = max(res, r - l + 1);
            // 更新当前字符最后一次出现的位置
            last[s[r]] = r;
        }
        return res;
    }
};
  • 记录不重复子串中有哪些字符
#include <vector>
#include <string>

using namespace std;

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        // 记录当前在滑动窗口中的字符
        vector<bool> entered(128, false);
        int res = 0;
        
        for (int l = 0, r = 0; r < s.length(); r++) {
            char ch = s[r];
            if (entered[ch]) {
                // 已经在窗口中,从窗口左边弹出元素,直到弹出 ch
                while (l < r && s[l] != ch) {
                    entered[s[l]] = false;
                    l++;
                }
                l++;
            } else {
                // 不在窗口中
                entered[ch] = true;
                res = max(res, r - l + 1);
            }
        }
        return res;
    }
};

76. 最小覆盖子串

#include <vector>
#include <string>

using namespace std;

class Solution {
public:
    // 题目默认如果存在则唯一
    string minWindow(string s, string t) {
        int lenS = s.length();
        int lenT = t.length();
        if (lenS < lenT) return "";

        // 负数表示 t 中字符在 s 中需要出现的次数,非负数表示其他字符在 s 中出现的次数
        vector<int> map(256, 0);
        for (char ch: t) map[ch]--;
        // 最小覆盖子串的长度
        int len = INT_MAX;
        // 最小覆盖子串的起始下标
        int start = 0;
        // 需要出现的各个字符的总个数
        int count = lenT;

        for (int r = 0, l = 0; r < lenS; ++r) {
            // 是 t 中的字符,则出现总次数减一
            if (map[s[r]] < 0) count--;
            // 当前字符出现次数加一
            map[s[r]]++;
            
            // 窗口已经包含 t 中所有字符,即 s 中已经出现了覆盖子串
            if (count == 0) {
                // 大于 0 说明可以从窗口左边移除一部分重复的字符,从而缩小窗口大小
                // 小于等于 0 时不能移除,否则就凑不齐t中的字符
                while (map[s[l]] > 0) {
                    map[s[l]]--;
                    l++;
                }
                if (r - l + 1 < len) {
                    // 记录窗口大小
                    len = r - l + 1;
                    // 记录窗口起始位置,用于返回最小覆盖子串
                    start = l;
                }
            }
        }
        if (len == INT_MAX) return "";
        return s.substr(start, len);
    }
};

134. 加油站

#include <vector>
#include <string>

using namespace std;

class Solution {
public:
    int canCompleteCircuit(vector<int> &gas, vector<int> &cost) {
        int n = gas.size();
        // 汽油余量前缀和
        int prefixSum = 0;
        // 当前窗口大小
        int len = 0;
        // 从每个位置开始尝试
        for (int l = 0, r; l < n; ++l) {
            while (prefixSum >= 0) {
                // 可以绕一圈
                if (len == gas.size()) return l;
                // 计算窗口右边界
                r = (l + len) % n;
                // 窗口扩大,r 移入窗口
                len++;
                // 到达 gas[r] 后,计算到达再后面一站的汽油余量
                prefixSum += gas[r] - cost[r];
            }
            // 移除窗口左侧
            len--;
            // 前缀和也要去除掉 l 位置的影响
            prefixSum -= gas[l] - cost[l];
        }
        return -1;
    }
};
#include <vector>

using namespace std;

class Solution {
public:
    int canCompleteCircuit(vector<int> &gas, vector<int> &cost) {
        int n = gas.size();
        // 从每个位置开始尝试
        for (int l = 0, r = 0, gasLeft; l < n; l = r + 1, r = l) {
            // 汽油余量
            gasLeft = 0;
            // 如果能到达下一站
            while (gasLeft + gas[r % n] - cost[r % n] >= 0) {
                // 可以绕一圈
                if (r - l == n) return l;
                // 到达 gas[r] 后的汽油余量
                gasLeft += gas[r % n] - cost[r % n];
                r++;
            }
        }
        return -1;
    }
};

1234. 替换子串得到平衡字符串

#include <vector>
#include <string>

using namespace std;

class Solution {
public:
    // 能否通过修改长度为 len 的滑动窗口,把字符串变成平衡字符串
    bool ok(vector<int> &counts, int len, int require) {
        for (int i = 0; i < 4; ++i) {
            // 窗口外的字符频率若超过 require,则无法消去多出来的字符
            if (counts[i] > require) return false;
            // 用长度 len 的窗口补齐每个字符缺失的个数 require - counts[i]
            len -= require - counts[i];
        }
        // 窗口刚好用完
        return len == 0;
    }

    int balancedString(string s) {
        int lenS = s.length();
        // 每种字符必须出现的次数
        int require = lenS / 4;
        // Q W E R转换成 0 1 2 3
        vector<int> nums(lenS);
        // 统计窗口外字符出现次数
        vector<int> counts(4, 0);
        for (int i = 0; i < lenS; ++i) {
            nums[i] = s[i] == 'Q' ? 0 : (s[i] == 'W' ? 1 : (s[i] == 'E' ? 2 : 3));
            counts[nums[i]]++;
        }

        // 最多调整整个数组
        int res = lenS;
        // 窗口 [l, r)
        for (int l = 0, r = 0; l < lenS; ++l) {
            while (!ok(counts, r - l, require) && r < lenS) {
                // 窗口右边移入,移入的字符在 counts 中的计数减一
                counts[nums[r]]--;
                r++;
            }
            if (ok(counts, r - l, require))
                res = min(res, r - l);
            // 窗口左边移出,移出的字符在 counts 中的计数加一
            counts[nums[l]]++;
        }
        return res;
    }
};
#include <vector>
#include <string>

using namespace std;

class Solution {
public:
    int balancedString(string s) {
        int lenS = s.length();
        // 每种字符必须出现的次数
        int require = lenS / 4;
        // Q W E R转换成 0 1 2 3
        vector<int> nums(lenS);
        // 统计窗口外字符出现次数
        vector<int> counts(4, 0);
        for (int i = 0; i < lenS; ++i) {
            nums[i] = s[i] == 'Q' ? 0 : (s[i] == 'W' ? 1 : (s[i] == 'E' ? 2 : 3));
            counts[nums[i]]++;
        }

        int debt = 0;
        for (int i = 0; i < 4; i++) {
            if (counts[i] < require) {
                // 低于需要出现的次数就改成 0
                counts[i] = 0;
            } else {
                // 高于需要出现的次数就改成负数,表示多出来的个数
                counts[i] = require - counts[i];
                // 计算一共多出来多少个
                debt -= counts[i];
            }
        }
        // 一个都没多,说明刚好平衡
        if (debt == 0) return 0;

        int res = INT_MAX;
        // todo
        for (int l = 0, r = 0; r < lenS; r++) {
            if (counts[nums[r]]++ < 0) debt--;
            if (debt == 0) {
                while (counts[nums[l]] > 0)
                    counts[nums[l++]]--;
                res = min(res, r - l + 1);
            }
        }
        return res;
    }
};

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

#include <vector>

using namespace std;

class Solution {
public:
    vector<int> counts;

    // 返回 nums 的所有子数组中,数字种类不超过 k 的子数组个数
    int numsOfMostKinds(vector<int> &nums, int k) {
        counts.clear();
        counts.resize(20001, 0);

        int res = 0;
        for (int l = 0, r = 0, types = 0; r < nums.size(); r++) {
            // 窗口右侧移入 nums[r]
            // 种类数加一
            if (counts[nums[r]] == 0) types++;
            // 词频加一
            counts[nums[r]]++;

            // 种类数超过了,需要从窗口左侧移除
            while (types > k) {
                // 词频减一
                counts[nums[l]]--;
                // 刚好把这个字符全部移除
                if (counts[nums[l]] == 0) types--;
                l++;
            }
            // 累加的是以 nums[r] 结尾,数字种类不超过 k 的子数组个数
            res += r - l + 1;
        }
        return res;
    }

    int subarraysWithKDistinct(vector<int> &nums, int k) {
        return numsOfMostKinds(nums, k) - numsOfMostKinds(nums, k - 1);
    }
};

395. 至少有 K 个重复字符的最长子串

#include <vector>
#include <string>

using namespace std;

class Solution {
public:
    int longestSubstring(string s, int k) {
        int len = s.length();
        vector<int> counts;

        int res = 0;
        // 子串只有 require 种字符,每种字符都必须大于等于 k 次,返回这样的最长子串
        for (int require = 1; require <= 26; ++require) {
            counts.clear();
            counts.resize(256, 0);

            // 窗口中字符种类总数
            int types = 0;
            // 窗口中字符出现次数大于等于 k 的种类总数
            int satisfy = 0;

            for (int l = 0, r = 0; r < len; r++) {
                counts[s[r]]++;
                // 新出现了一个种类
                if (counts[s[r]] == 1) types++;
                // 新达标了一个种类
                if (counts[s[r]] == k) satisfy++;

                // 字符种类超了,开始移除左边字符
                while (types > require) {
                    if (counts[s[l]] == 1) types--;
                    if (counts[s[l]] == k) satisfy--;
                    // 窗口左侧移出字符
                    counts[s[l]]--;
                    l++;
                }
                // 子串以 r 位置结尾,且种类等于 require 的最大长度
                if (satisfy == require) res = max(res, r - l + 1);
            }
        }
        return res;
    }
};

标签:字符,窗口,nums,int,res,++,滑动,counts
From: https://www.cnblogs.com/sprinining/p/18450197

相关文章

  • [Electron] 应用不关闭窗口退出而是保留到后台运行
    import{app,BrowserWindow,Tray,Menu}from"electron";import{fileURLToPath}from"url";importpathfrom"path";const__filename=fileURLToPath(import.meta.url);const__dirname=path.dirname(__filename);lettray......
  • Dijkstra算法,动态规划和滑动窗口
    一:最小花费题目链接:1928.规定时间内到达终点的最小花费-力扣(LeetCode)(1)Dijkstra算法理解问题:首先,我们需要理解问题的核心是找到一条从城市0到城市n-1的路径,这条路径在不超过给定时间 maxTime 的前提下,通行费之和最小。图的表示:由于城市之间是通过双向道路连接的......
  • 从2023济南K学习滑动窗口中位数问题
    板子对顶堆template<classT>structDualHeap{Heap<T,std::greater<T>>small;//小根堆,里面存放大的值Heap<T,std::less<T>>big;//大根堆,里面存放前k小的值//中位数就是big.top()DualHeap(){}voidupdate(){if(b......
  • pycharm 拆分窗口, 取消分屏; VS code 分屏
    SplitVertically或者SplitHorizontally可以把当前编辑窗口垂直或者水平拆分成两个。 SplitVertically或者SplitHorizontally可以把当前编辑窗口垂直或者水平拆分成两个。 取消拆分窗口: VScode分屏: ......
  • P1502 窗口的星星(扫描线)
    关键在把矩形框点转化为点的影响放大为矩形,此时转变为求一个点的权值最大#include<bits/stdc++.h>usingnamespacestd;#definexfirst#defineysecondtypedefpair<int,int>PII;typedeflonglongll;typedefunsignedlonglongull;typedefunsignedintuint;type......
  • 滑动窗口->dd爱框框
    1.题目:  2.题解:2.1为什么用滑动窗口优化:因为元素都是大于0的所以:当找到大于等于x的值时,right可以不用返回两个指针都往后走;因此可以使用滑动窗口优化暴力解法  2.2:滑动窗口具体使用步骤:1.进窗口:sum+=array[right];2.判断:sum>=x时出窗口    灵活......
  • pbootcms模板导航设置外链时新窗口打开
    要在PbootCMS中设置导航链接并在新窗口中打开外部链接,可以使用以下方法。具体步骤如下:修改导航标签添加条件判断示例代码以下是完整的示例代码,展示了如何在导航链接中添加条件判断,以便在新窗口中打开外部链接:{pboot:nav}<ahref="[nav:link]"{pboot:if('[nav:ou......
  • Prism 弹出窗口
    1、简单实现①、创建用户控件跨模块的窗口弹出,只需要创建窗口的内容即可,也就是用户控件,这里是在Views文件夹下,创建DialogContentView用户控件。需要注意的是,默认情况下,如果需要对弹出窗口进行样式设置的话,需要通过prism:Dialog.WindowStyle来进行设置。<UserControlx:Class="......
  • Halcom与C#窗口应用联合问题一:DLL“halcon”:找不到指定的模块。
     问题如下:原因是:        如果你在解决方案资源管理器中的引用下添加了halcondotnet.dll文件,有可能在工具箱下没有自动添加。在工具箱项里面没有HWindowControl工具。解决方法如下:                1.在工具箱中右击任何一个工具,然后点击......
  • MySQL窗口函数总结(三)
    MySQL窗口函数(WindowFunctions)是一种高级的SQL查询技巧,它允许在结果集的一组相关行上执行计算。窗口函数可以用于处理分组、排序、累计等复杂的聚合任务,使得查询更加简洁和高效。在MySQL8.0及更高版本中,支持窗口函数。以下是一些常用的窗口函数:ROW_NUMBER():为结果集中的......