【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);
    }
};
相关推荐
得物技术12 分钟前
从狂野代码到按目标生产:得物推荐 AI Harness 的工程化实践|AICon 演讲整理
人工智能·算法·架构
AI小老六4 小时前
SkillOpt 架构拆解:把 Skill 文本当参数,用执行轨迹训练 Agent
后端·算法·ai编程
胡萝卜术4 小时前
从“分数打架”到“排名投票”:为什么你的ChatBI必须用RRF?
算法·设计模式·面试
Asize5 小时前
初识DFS 与 BFS:递归、队列与图遍历
算法
罗西的思考18 小时前
机器人 / 强化学习】HIL-SERL:人类在环驱动的具身智能进化框架
人工智能·算法·机器学习
美团技术团队1 天前
LongCat 开源 VitaBench 2.0:长期动态智能体基准新标杆
人工智能·算法
用户805533698031 天前
不止三件套:QObject 属性系统全关键字与运行时反射!
c++·qt
To_OC2 天前
LC 207 课程表:刚学图论那会儿,我连这是拓扑排序都没看出来
javascript·算法·leetcode
To_OC2 天前
LC 208 实现 Trie 前缀树:曾被名字劝退,写完发现是送分题
javascript·算法·leetcode
BadBadBad__AK2 天前
线段树维护区间 k 次方和
c++·数学·算法·stl