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

相关推荐
happyprince1 分钟前
01-整体观-Codex全貌解读(源码)
算法·ai编程
俊基科技23 分钟前
别自己写算法了,让这颗37.5毫米的模组替你“闭嘴干活”
神经网络·算法·硬件开发·ai降噪·语音模块·回音消除
_Narcissus_1 小时前
动态规划初探(含完整代码实现)
c语言·数据结构·c++·算法·动态规划·背包问题·最短路径
世人万千丶1 小时前
鸿蒙项目实战 - 社区活动编排板:标签云布局算法与自动换行
学习·算法·华为·harmonyos·鸿蒙
zander2581 小时前
LeetCode 279. 完全平方数
算法·深度优先
典典分享指南2 小时前
VS Code Git 工作树:解锁多分支并行开发新体验
算法·决策树·逻辑回归·启发式算法
_Narcissus_3 小时前
枚举和模拟算法笔记
c语言·数据结构·c++·笔记·算法·模拟·枚举
天疆说3 小时前
策略的进化:从随心所欲到稳健前行
算法
冻柠檬飞冰走茶3 小时前
《数据结构实验指导-C++语言版》 在顺序表 list 中查找元素 x
开发语言·数据结构·c++·算法·list