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")); // ""
相关推荐
橘子星1 小时前
别再懵圈!JS 执行机制的 “千层套路” 全揭秘
前端·javascript
YHHLAI1 小时前
前端 HTTP 请求 & LLM 接口开发
前端·网络协议·http
拾年2751 小时前
__proto__ vs prototype:90% 的人分不清的 JavaScript 核心
前端·javascript·面试
国科安芯1 小时前
国科安芯推出商业航天级抗辐照半双工 RS485 收发器 ASC485S2Y
前端·单片机·嵌入式硬件·架构·安全性测试
丑过三八线1 小时前
Umi 运行时配置 app.tsx 详解
前端
提子拌饭1331 小时前
个人月事记录表应用 - 鸿蒙PC Electron框架完整实现指南
前端·javascript·华为·electron·前端框架·开源·鸿蒙系统
一只齐刘海的猫1 小时前
【Leetcode】移动零
算法·leetcode·职场和发展
神奇小汤圆1 小时前
SEATA:Server 到 Golang Client 全链路走读
面试
超人气王1 小时前
新手学前端JS浅拷贝和深拷贝:对象复制竟然是个“替身文学”?
javascript·面试