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 的关键。

相关推荐
weixin_BYSJ19871 小时前
springboot旅游管理系统04470(附源码+开发文档+部署教程)
java·spring boot·python·算法·django·flask·旅游
Bingorl1 小时前
机器学习之朴素贝叶斯算法
人工智能·算法·机器学习
8Qi81 小时前
LeetCode 209. 长度最小的子数组(Minimum Size Subarray Sum)
java·算法·leetcode·双指针·滑动窗口
丘山望岳1 小时前
二叉搜索双壁——map和set
开发语言·数据结构·c++
狮子座明仔1 小时前
DeCoRL:把推理链拆成“乐团合奏“——AAAI 2026 一篇把 RLHF 推到 32B 打 GPT-4o 的工作
人工智能·深度学习·算法
QiLinkOS1 小时前
合肥气链科技有限公司创办与未来技术应用
c语言·数据结构·c++·人工智能·单片机·嵌入式硬件·算法
妄想出头的工业炼药师2 小时前
追踪定位大模型
算法·开源
Solis程序员2 小时前
TreeMap 核心原理与实战
java·数据结构·算法
Dlrb12112 小时前
数据结构-内核链表
linux·数据结构·链表·内核链表·inline·容器宏