【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);
    }
};
相关推荐
像素猎人2 小时前
蓝桥杯OJ2049蓝桥勇士【动态规划】【dp[n]不是符合题意的答案,只是以an结尾的子问题的答案】
c++·算法·蓝桥杯·动态规划·区间dp
羊小猪~~2 小时前
LLM--SFT简介
python·考研·算法·ai·大模型·llm·微调
广州灵眸科技有限公司2 小时前
瑞芯微(EASY EAI)RV1126B 人脸98关键点算法识别
开发语言·科技·嵌入式硬件·物联网·算法·php
篮子里的玫瑰2 小时前
FreeRTOS:信号量与互斥量在DMA串口发送中的实战剖析
stm32·单片机·嵌入式硬件·算法
hughnz2 小时前
钻头技术持续突飞猛进:地热钻探领域的创新
人工智能·算法
xiaoye-duck2 小时前
《算法题讲解指南:动态规划算法--子数组系列》--21.乘积最大子数组,22.乘积为正数的最长子数组
c++·算法·动态规划
MicroTech20252 小时前
突破非幺正动力学瓶颈:MLGO微算法科技量子虚时演化赋能开放量子系统模拟
科技·算法·量子计算
计算机安禾2 小时前
【数据结构与算法】第24篇:哈夫曼树与哈夫曼编码
c语言·开发语言·数据结构·c++·算法·visual studio
wsoz2 小时前
Leetcode双指针-day2
算法·leetcode