刷LeetCode遇到第3题时,很多人第一反应是暴力枚举。两个嵌套循环,检查每个子串有没有重复字符,时间复杂度直接爆炸。但换个思路,这道题其实可以在线性时间内解决——核心就一句话:遇到重复字符时,别重启,只把左边界往前挪。
这就是滑动窗口的精髓。想象一个左右指针夹着的区间,右指针负责扩张领地,左指针负责清理门户。窗口里装了什么?一个集合,只存当前窗口内的字符,查重快如闪电。
具体怎么操作?右指针从左往右扫,每遇到新字符先查集合。如果没重复,直接加入,窗口变大;如果重复了,左指针开始干活——从左边逐个踢人,直到那个重复的字符被清出去,再把新字符请进来。整个过程两个指针都只往右走,绝不回头。
拿示例"abcabcbb"走一遍。初始左右都在0,'a'进集合,窗口长1。右移到1,'b'新鲜,窗口变2。右移到2,'c'也新,窗口到3,目前最大。右移到3又遇到'a',集合里已有'a',左指针开始右移:先踢掉索引0的'a',左变1,现在'a'可以进来了,窗口变成1到3,长度还是3。
继续走:右到4是'b',集合里有'b',左指针踢掉索引1的'b',窗口2到4。右到5是'c',再踢再移。右到6还是'b',这次得连踢两个——先踢'a','b'还在;再踢'b',终于干净,窗口缩到5到6。全程最大长度锁定在3。
复杂度方面,每个字符进集合一次、出集合一次,时间复杂度O(n),空间复杂度也是O(n)——集合最坏情况下装整个字符串的字符。代码实现干净利落:初始化空集合、左指针0、最大长度0;右指针遍历字符串,内层while清重复,然后加字符、更新最大值。
常见坑点:有人习惯先加字符再检查,结果字符匹配到自己。正确顺序是先查后加,或者加完之后发现重复再处理——但前者更直觉。另一个细节是窗口长度的计算:right - left + 1,别忘了那个+1,索引差值和长度差1。
这套模式不只解这一题。最小覆盖子串、找到字符串中所有字母异位词、水果成篮——LeetCode上十几道中等题都是同一个套路。右扩左缩,集合或哈希表当窗口的记账本,线性扫描替代暴力枚举。理解了这个,面试遇到字符串子串问题,心里就有底了。
热门跟贴