minwindow不能只靠左指针收缩一次,必须循环收缩并验证是否仍满足覆盖条件;需用need/have映射和formed计数器精准维护字符匹配状态,并在formed达标后持续左移、更新、判断。

为什么 minWindow 不能只靠左指针收缩?
很多人写完滑动窗口逻辑后,发现返回空字符串或长度不对,根本原因在于:右指针扩展满足条件后,左指针收缩时没做「最小化验证」。比如目标串是 "ABC",当前窗口 "AABBC" 满足覆盖,但去掉最左的 'A' 后仍是 "ABBC",依然覆盖 —— 这步必须循环判断,不能只收缩一次。
实操建议:
- 用
map[byte]int统计t中各字符需求数(need),再维护一个同结构的have记录窗口内已匹配数 - 引入整型变量
formed,表示有多少种字符达到了需求数(避免每次遍历 map 判断) - 左指针移动必须放在
formed == len(need)成立后的 for 循环里,每次移出字符后更新have和formed
如何正确更新 have 和 formed?
错误写法是删掉字符就直接 have[c]--,然后看是否 have[c] 就减 <code>formed —— 这会漏掉重复字符的临界点。比如 need['A']=2,窗口里原来有 3 个 'A',删掉 1 个后还剩 2,仍满足;再删 1 个才降到 1,此时才该减 formed。
实操建议:
- 右指针加入字符
c时:have[c]++,若have[c] == need[c],则formed++ - 左指针移出字符
c时:先判断have[c] == need[c],成立才formed--,再执行have[c]-- -
need中只存t出现过的字符,对未出现字符(如have['X'])不做任何处理,避免干扰
minWindow 返回空字符串的常见触发条件
不是没找到就返回空,而是三种情况之一发生时必须返回空:s 长度小于 t、t 有 s 完全不包含的字符、滑动结束后 formed 始终没达到 len(need)。
实操建议:
- 初始化时先做快速校验:
if len(s) - 构建
need后,遍历t每个字节,若need[c] > 0但s中完全没出现过(可通过预扫描s的 byte map 判断),直接返回"" - 主循环结束仍未更新过最小长度(即
minLen == math.MaxInt32),说明无解,返回""
Go 中字符串切片和字节操作的边界注意点
Go 的 string 是只读字节序列,s[i:j] 虽然高效,但窗口起止位置必须严格基于字节索引(不是 rune)。如果 t 含 UTF-8 多字节字符,而题目明确说「字符串由英文字母、数字、符号组成」,那直接按 []byte 处理没问题;但一旦需求变成「支持中文」,就必须转 []rune,此时窗口长度、索引计算全要重写。
实操建议:
- 全程用
[]byte(s)转换,避免反复调用string()构造新字符串 - 记录最优窗口用
bestLeft, bestRight int,最后统一切片:string(s[bestLeft:bestRight+1]) - 不要在循环里拼接字符串(如
res += s[i:j]),Go 的字符串不可变,会频繁分配内存
双指针滑动窗口真正难的不是移动逻辑,而是每一步增减字符时,have 和 need 的等号判定时机 —— 差一个等于号,结果就全错。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











