LeetCode 76. 最小覆盖子串(Minimum Window Substring)

LeetCode 76. 最小覆盖子串(Minimum Window Substring)

本题难度:困难

考察点:滑动窗口 / 哈希表 / Java 包装类陷阱


🔗 题目链接

👉 https://leetcode.com/problems/minimum-window-substring/


📖 题目描述

给定字符串 st,请在 s 中找出包含 t 所有字符的最小子串。

示例

复制代码
输入:
s = "ADOBECODEBANC"
t = "ABC"

输出:
"BANC"

说明

  • 如果 s 中不存在这样的子串,返回空字符串 ""
  • t 中可能包含重复字符

💡 解题思路(滑动窗口)

核心思想

使用 双指针 + 哈希表 维护一个滑动窗口:

  • need:记录 t 中每个字符需要的次数
  • window:记录当前窗口中每个字符出现的次数
  • scnt:当前窗口中已经满足 need 要求的字符种类数
  • tcnt:目标字符种类数

算法流程

  1. 统计 t 中字符频次 → need
  2. 初始化双指针 l = 0, r = 0
  3. 右指针 r 向右扩展窗口
    • 若字符属于 t,更新 window
    • 若某个字符数量达到 needscnt++
  4. scnt == tcnt
    • 尝试收缩左指针
    • 更新最小窗口
  5. 返回最小窗口子串

✅ AC 代码(Java)

java 复制代码
class Solution {
    public String minWindow(String s, String t) {
        char[] S = s.toCharArray();
        HashMap<Character, Integer> window = new HashMap<>();
        HashMap<Character, Integer> need = new HashMap<>();

        // 统计 t 中字符频次
        for (char ch : t.toCharArray()) {
            need.put(ch, need.getOrDefault(ch, 0) + 1);
        }

        int tcnt = need.size();     // 目标字符种类数
        int scnt = 0;               // 当前满足条件的字符种类数
        int cur = 0;                // 最小窗口起点
        int reslen = Integer.MAX_VALUE;

        int l = 0, r = 0;

        while (r < s.length()) {
            char cr = S[r];
            if (need.containsKey(cr)) {
                window.put(cr, window.getOrDefault(cr, 0) + 1);
                // ✅ 必须用 equals,不能用 ==
                if (window.get(cr).equals(need.get(cr))) {
                    scnt++;
                }
            }

            // 窗口满足条件,开始收缩
            while (scnt == tcnt) {
                if (r - l + 1 < reslen) {
                    reslen = r - l + 1;
                    cur = l;
                }

                char cl = S[l];
                if (need.containsKey(cl)) {
                    window.put(cl, window.get(cl) - 1);
                    // ✅ 字符数量不足
                    if (need.get(cl) > window.get(cl)) {
                        scnt--;
                    }
                }
                l++;
            }
            r++;
        }

        return reslen == Integer.MAX_VALUE ? "" : s.substring(cur, cur + reslen);
    }
}

⏱ 复杂度分析

项目 复杂度
时间复杂度 O(n)
空间复杂度 O(字符集) ≈ O(128)

✅ 每个字符最多进出窗口一次


⚠️ 注意事项(非常重要)

1️⃣ Integer 比较必须用 equals

❌ 错误:

java 复制代码
window.get(c) == need.get(c)

✅ 正确:

java 复制代码
window.get(c).equals(need.get(c))

📌 原因

Integer 仅在 [-128, 127] 有缓存,超过会新建对象,== 会失效。


2️⃣ 必须先更新答案,再收缩窗口

java 复制代码
if (r - l + 1 < reslen) {
    reslen = r - l + 1;
    cur = l;
}

否则会错过最优解。


3️⃣ HashMap value 可能为 null

  • 使用 getOrDefault
  • 避免 NullPointerException

4️⃣ 返回 substring 时注意区间

java 复制代码
s.substring(cur, cur + reslen)

✅ 左闭右开,符合 Java 规范


🧠 面试加分点

  • 明确说出 滑动窗口收缩条件
  • 指出 Integer == 的坑
  • 提到 时间复杂度 O(n)
  • 能现场手写出 AC 版本

✅ 总结一句话

滑动窗口 + 哈希表统计频次,Integer 比较一定用 equals,这是本题 100% AC 的关键。

相关推荐
JieE21216 小时前
LeetCode 101. 对称二叉树|JS 递归 + 迭代双解法,彻底搞懂镜像判断
javascript·算法
JieE2122 天前
LeetCode 56. 合并区间|超清晰 JS 图解思路,面试高频区间题
javascript·算法·面试
Jack202 天前
HarmonyOS开发中错误处理策略:网络异常统一处理
算法
小小杨树2 天前
读懂色彩:拍照调色不再难
算法·计算机视觉·配色
JieE2123 天前
LeetCode 226. 翻转二叉树|JS 递归超详细拆解,二叉树入门经典题
javascript·算法
JieE2123 天前
LeetCode 104. 二叉树的最大深度|递归思路超详细拆解
javascript·算法
vivo互联网技术3 天前
CVPR 2026 | 全新强化学习框架 BeautyGRPO:重塑真实人像
算法·大模型·cvpr·影像
Darling噜啦啦3 天前
列表转树算法深度解析:从 Map 到 Reduce 的两种实现,面试高频考点
数据结构·算法·面试
用户497863050733 天前
(一)小红的数组操作
算法·编程语言