LeetCode 76 最小覆盖子串|JS 滑动窗口标准解法

大家好,这篇文章用来记录 LeetCode 76 最小覆盖子串 的 JS 标准解法,这道题是滑动窗口的经典必做题,面试频率极高。

我会直接给出可 AC 代码,并逐行详细解释,方便自己复习也分享给大家。

题目简介

给两个字符串 st,找出 s包含 t 所有字符的最短子串 。 如果没有,返回空字符串 ""


完整可提交代码

javascript 复制代码
var minWindow = function(s, t) {
    const need = {};
    const window = {};
    let valid = 0;
    let left = 0, right = 0;
    let start = 0, len = Infinity;

    // 统计 t 中每个字符需要的数量
    for (const c of t) {
        need[c] = (need[c] || 0) + 1;
    }
    // 需要凑齐的字符种类数
    const needSize = Object.keys(need).length;

    // 右指针遍历整个字符串
    while (right < s.length) {
        const c = s[right];
        right++;

        // 如果当前字符是我们需要的
        if (need[c]) {
            window[c] = (window[c] || 0) + 1;
            // 某一类字符数量达标,valid+1
            if (window[c] === need[c]) {
                valid++;
            }
        }

        // 当窗口已经满足所有条件,开始收缩左侧
        while (valid === needSize) {
            // 更新最小窗口
            if (right - left < len) {
                start = left;
                len = right - left;
            }

            // 准备移除左指针字符
            const d = s[left];
            left++;

            // 如果是需要的字符,判断是否会破坏 valid
            if (need[d]) {
                if (window[d] === need[d]) {
                    valid--;
                }
                window[d]--;
            }
        }
    }

    // 没有找到返回空,否则返回截取的子串
    return len === Infinity ? "" : s.slice(start, start + len);
};

逐行详细解析

1. 变量定义

javascript 复制代码
const need = {};     // 记录 t 中每个字符需要多少个
const window = {};   // 记录当前窗口里每个字符有多少个
let valid = 0;       // 记录已经"数量达标"的字符种类数
let left = 0, right = 0; // 滑动窗口双指针
let start = 0, len = Infinity; // 记录最终最短子串的起点和长度
  • need:我们要找的字符目标数量
  • window:当前窗口内的字符数量
  • valid核心判断条件,表示有多少种字符已经满足数量要求
  • left/right:窗口左、右边界
  • start/len:保存最优解,避免反复截取字符串

2. 统计目标字符

javascript 复制代码
for (const c of t) {
    need[c] = (need[c] || 0) + 1;
}
const needSize = Object.keys(need).length;
  • 遍历 t,统计每个字符需要多少个
  • needSize一共需要凑齐多少种字符

3. 右指针扩大窗口

javascript 复制代码
while (right < s.length) {
    const c = s[right];
    right++;

    if (need[c]) {
        window[c] = (window[c] || 0) + 1;
        if (window[c] === need[c]) {
            valid++;
        }
    }
  • 右指针一直往右走,扩大窗口
  • 遇到需要的字符 ,就加入 window 计数
  • 当某字符数量刚好达标 时,valid += 1

4. 左指针收缩窗口(核心)

javascript 复制代码
while (valid === needSize) {
    // 更新最小窗口
    if (right - left < len) {
        start = left;
        len = right - left;
    }

    const d = s[left];
    left++;

    if (need[d]) {
        if (window[d] === need[d]) {
            valid--;
        }
        window[d]--;
    }
}
  • valid === needSize,说明当前窗口已经包含了 t 所有字符
  • 这时尽可能缩小窗口,寻找更短的子串
  • 每次移动左指针前:
    • 先更新最小窗口记录
    • 再把左边字符移出窗口
    • 如果移出后导致某字符不满足数量valid--,退出循环

5. 返回结果

javascript 复制代码
return len === Infinity ? "" : s.slice(start, start + len);
  • 如果 len 还是无穷大,说明没找到,返回空串
  • 否则返回记录的最短子串

核心思想总结

这道题的核心就是 滑动窗口 + 哈希计数

  1. 用右指针扩大窗口,直到满足条件
  2. 用左指针收缩窗口,直到不满足条件
  3. 全程记录最小窗口
  4. valid 精准判断窗口是否合法

这套模板可以通杀绝大多数子串滑动窗口题,非常实用。


测试用例

javascript 复制代码
console.log(minWindow("ADOBECODEBANC", "ABC")); // "BANC"
console.log(minWindow("a", "a")); // "a"
console.log(minWindow("a", "aa")); // ""
相关推荐
圣光SG7 分钟前
Java Web入门基础知识笔记
java·前端·笔记
cyforkk1 小时前
Vercel 绑定自定义域名极简配置指南
服务器·前端·网络
什巳2 小时前
JAVA练习306- 翻转二叉树
java·数据结构·算法·leetcode
smj2302_796826522 小时前
解决leetcode第3989题网格中保持一致的最大列数
python·算法·leetcode
IT_陈寒2 小时前
React useEffect依赖数组中埋的坑,这次终于让我逮到了
前端·人工智能·后端
FourAu3 小时前
2026 前端突围指南:从 ESR 边缘渲染到封装 Web AI SDK,聊聊 AI 时代的职业进化
前端·人工智能
Zkeq3 小时前
不止是聊天框:我用 EdgeOne Makers Agents 给烹饪 APP 加了一位能“改菜谱”的 AI 主厨
前端
巧克力男孩dd3 小时前
Python超典型练习题(第一次作业)
开发语言·python·算法
爱刷碗的苏泓舒4 小时前
平方根信息滤波:矩阵推导及 GNSS 参数估计应用
线性代数·算法·矩阵·gnss·参数估计·测量平差·平方根信息滤波