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")); // ""
相关推荐
变量未定义~7 分钟前
最小生成树1(Prim模板)、最小生成树2(kruskal模板)、最近公共祖先(模板)
算法
这就是佬们吗10 分钟前
Python入门⑤-异常处理、文件操作与实战项目
开发语言·数据库·python·算法·pycharm
LONGZETECH18 分钟前
新能源汽车充电设备装配与调试仿真教学软件 技术架构与核心实现解析
大数据·算法·3d·unity·架构·汽车
自学小白菜20 分钟前
关于提升机器学习算法做预测回归任务精度的方法分享
算法·机器学习·回归·提升精度
学废了wuwu28 分钟前
从入门到实战:深入浅出模型压缩三大核心——剪枝、彩票假设与知识蒸馏
人工智能·算法·剪枝
小肝一下39 分钟前
3. 单链表
c语言·数据结构·c++·算法·leetcode·链表·dijkstra
劈星斩月1 小时前
监督学习算法 之 决策树
学习·算法·决策树
学计算机的计算基1 小时前
操作系统内存管理全解:虚拟内存、页表、COW、malloc、OOM一篇搞定
java·笔记·算法
tachibana21 小时前
hot100 前 K 个高频元素(347)
java·数据结构·算法·leetcode
To_OC9 小时前
LC 131 分割回文串:刚学回溯时,我连怎么切字符串都想不明白
javascript·算法·leetcode