【Hot 100 刷题计划】 LeetCode 76. 最小覆盖子串 | C++ 滑动窗口题解

LeetCode 76. 最小覆盖子串 | C++ 滑动窗口高阶模板题解

📌 题目描述

题目级别:困难 (Hard)

给定两个字符串 st,长度分别是 mn。返回 s 中的 最短窗口 子串,使得该子串包含 t 中的每一个字符(包括重复字符)。如果没有这样的子串,返回空字符串 ""

测试用例保证答案唯一。

  • 示例:
    输入:s = "ADOBECODEBANC", t = "ABC"
    输出:"BANC"
    解释:最小覆盖子串 "BANC" 包含来自字符串 t'A''B''C'

💡 解题思路:滑动窗口通用模板

寻找满足某种条件的"最短子串",是滑动窗口 的绝对主场。这道题的核心难点在于:如何快速判断当前窗口内是否已经凑齐了 t 中的所有字符?

我们引入两个哈希表 needwindows,以及一个极其巧妙的变量 valid

  • need:记录字符串 t 中每个字符需要的数量。
  • windows:记录当前滑动窗口中,这些目标字符出现的数量。
  • valid:记录当前窗口中,已经满足数量要求的字符种类数 。当 valid == need.size() 时,说明窗口已经完全覆盖了 t

核心运作机制:两步走战略

  1. 步骤一:向右扩张(寻找可行解)

    右指针 r 不断向右移动,把新字符加入 windows。如果加入的字符恰好是 need 中需要的,并且数量刚好达标,我们就让 valid++
    目标:一直向右扩张,直到 valid == need.size(),此时我们找到了一个"可行解"(虽然可能很长)。

  2. 步骤二:向左收缩(优化最优解)

    一旦窗口满足了条件(valid == need.size()),我们就暂存当前的子串长度和起始位置,并尝试逼近极限

    左指针 l 开始向右移动,把字符从窗口中"吐出来"。如果吐出的字符恰好是关键字符,且导致窗口内该字符的数量低于了 need 的要求,那 valid--,窗口不再合法。
    目标:剥离边缘的无用字符,找到满足条件的"最短"长度。

窗口就在这样"扩张 -> 满足条件 -> 收缩 -> 不满足条件 -> 再次扩张"的博弈中,不断刷新最短记录,直到遍历完整个字符串。


💻 C++ 代码实现

cpp 复制代码
class Solution {
public:
    string minWindow(string s, string t) {
        // need 记录目标字符串 t 中每个字符的需求量
        // windows 记录当前窗口中对应字符的拥有量
        unordered_map<char, int> windows, need;
        for (char c : t) need[c]++;

        int l = 0, r = 0;
        int valid = 0; // 记录窗口中满足 need 条件的字符种类数
        
        // 记录最小覆盖子串的起始索引及长度
        int st = 0, len = INT_MAX;

        while (r < s.size()) {
            // c 是将移入窗口的字符,同时右指针向右移动
            char c = s[r++];

            // 进行窗口内数据的一系列更新
            if (need.count(c)) {
                windows[c]++;
                // 当该字符的数量刚好达到目标要求时,满足条件的种类数 +1
                if (windows[c] == need[c]) {
                    valid++;
                }
            }

            // 判断左侧窗口是否要收缩 (当所有的字符种类都达标时)
            while (valid == need.size()) {
                // 在这里更新最小覆盖子串的记录
                if (r - l < len) {
                    st = l;
                    len = r - l;
                }

                // d 是将移出窗口的字符,同时左指针向右移动
                char d = s[l++];

                // 进行窗口内数据的一系列更新
                if (need.count(d)) {
                    // 如果移出的字符是关键字符,且移出后数量不达标了,种类数 -1
                    if (windows[d] == need[d]) {
                        valid--;
                    }
                    windows[d]--;
                }
            }
        }

        // 返回最小覆盖子串,如果没有找到则返回空字符串
        return len == INT_MAX ? "" : s.substr(st, len);
    }
};
相关推荐
.道阻且长.4 小时前
2.LeetCode算法习题讲解--双指针--复写零
算法·leetcode·职场和发展
·薯条大王5 小时前
经济实惠玩云服务器|一台云服务器多人共用,子账号配置教程
java·linux·运维·服务器·汇编·c++·python
To_OC6 小时前
LC 438 找到所有字母异位词:暴力超时后,我靠滑动窗口一招搞定
javascript·算法·leetcode
Forever Nore9 小时前
学完C语言力扣第一题做不来正常吗
数据结构·算法
hansang_IR10 小时前
【题解】LC:倍增 / 区间并查集(Range Parallel Unionfind)
c++·算法·并查集
东东最爱敲键盘10 小时前
day7.c++继承
c++
沫璃染墨10 小时前
《从零入门Linux系统篇(十五):系统工具篇·六——GDB调试器详解:从程序执行控制到高级调试技巧》
linux·运维·服务器·c语言·c++
吃着火锅x唱着歌10 小时前
Effective C++ 学习笔记 条款40 明智而审慎地使用多重继承
c++·笔记·学习
Tisfy11 小时前
LeetCode 3731.找出缺失的元素:哈希 / 排序
算法·leetcode·哈希算法·排序·哈希表