LeetCode 76 最小覆盖子串|JS 滑动窗口标准解法(逐行精讲)

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

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

题目简介

给两个字符串 s 和 t,找出 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")); // ""
相关推荐
倒头就睡的小比特3 天前
算法竞赛C++常用的STL
c++·算法
小羊没烦恼!3 天前
初探性能优化——2个月到4小时的性能提升
java·开发语言·windows·算法·c#
三十而立洋3 天前
Cookie 详解:从产生到安全,一次讲透
前端·javascript
猎头南楼3 天前
知识社区推荐系统实践:新用户冷启动与长短期兴趣建模的挑战 资深推荐算法工程师
人工智能·深度学习·算法·机器学习
卡布鲁3 天前
把一个 Vite + Vue3 应用塞进 qiankun (React + Umi3) 主站:十个坑的复盘
前端·javascript·react.js
李少兄3 天前
JavaScript 隐式全局变量解析
javascript
旖旎夜光3 天前
力控面试题 01.01: 判定字符是否唯一(位运算) —— 题解
c++·学习·算法·leetcode·力控
wzdark3 天前
大规模并行计算中的负载均衡算法研究4
算法
Because_of_Her13 天前
并查集-听课笔记
笔记·算法·并查集
码流子3 天前
高速公路安全监测实践:碰撞监测预警+物联网底座,从感知到处置的闭环
大数据·人工智能·物联网·算法·架构