LeetCode 76. 最小覆盖子串(Minimum Window Substring)
✅ 本题难度:困难
✅ 考察点:滑动窗口 / 哈希表 / Java 包装类陷阱
🔗 题目链接
👉 https://leetcode.com/problems/minimum-window-substring/
📖 题目描述
给定字符串 s 和 t,请在 s 中找出包含 t 所有字符的最小子串。
示例
输入:
s = "ADOBECODEBANC"
t = "ABC"
输出:
"BANC"
说明
- 如果
s中不存在这样的子串,返回空字符串"" t中可能包含重复字符
💡 解题思路(滑动窗口)
核心思想
使用 双指针 + 哈希表 维护一个滑动窗口:
need:记录t中每个字符需要的次数window:记录当前窗口中每个字符出现的次数scnt:当前窗口中已经满足need要求的字符种类数tcnt:目标字符种类数
算法流程
- 统计
t中字符频次 →need - 初始化双指针
l = 0, r = 0 - 右指针
r向右扩展窗口- 若字符属于
t,更新window - 若某个字符数量达到
need,scnt++
- 若字符属于
- 当
scnt == tcnt:- 尝试收缩左指针
- 更新最小窗口
- 返回最小窗口子串
✅ 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 的关键。