§1
题目描述
给两个字符串 s1、s2,判断 s2 中是否存在一个子串,是 s1 的排列。
s1 = "ab"
s2 = "eidbaooo"
输出 = true
§2
思路解析动画文字版
窗口长度始终等于 len(s1),每右移一格就加右字符、删左字符。
目标计数:排列不看顺序,只看计数。
窗口 ei:长度已经是 2,但字符不对。
右移到 id:定长窗口每次只做一进一出。
右移到 db:计数还没对上。
右移到 ba:顺序不同没关系,计数相同就是一个排列。
返回 true:一旦命中就可以结束。
负例窗口 ao:不是每个含 a 的窗口都匹配,还要 b 的数量也对。
长度不够时:定长窗口常见边界:还没凑够长度时不要比较。
收尾:本例在 ba 处窗口频次匹配 → 返回 true。若扫完整个 s2 都没有任一长度窗口的频次等于 s1,则返回 false。
看到“是否包含某个排列/异位词”,就想到固定长度滑动窗口。
边界三连:三种边界先想清楚。
面试官常追问:三个高频追问,答法记牢。
§3
参考代码
class Solution: def checkInclusion(self, s1, s2): if len(s1) > len(s2): return False need = [0]*26; win = [0]*26 for ch in s1: need[ord(ch)-97] += 1 for i, ch in enumerate(s2): win[ord(ch)-97] += 1 if i >= len(s1): # 滑出最左 win[ord(s2[i-len(s1)])-97] -= 1 if win == need: return True # 频次相等=排列 return False看懂代码不等于写得出。盖住上面,自己默写一遍试试。卡在哪一行说不清,就点右下角问小欧。
§4
复杂度
- 时间复杂度:O(n),窗口滑过 s2 一遍,每步比较 O(26)=O(1)
- 空间复杂度:O(1),两个 26 大小的频次数组
§5
可套用的代码模板
这段代码可以按 LeetCode Python 模板直接提交;变量名和动画里的核心状态保持一致。
Python
class Solution: def fixedWindow(self, s, k): win = [0] * 26 for i, ch in enumerate(s): win[ord(ch) - 97] += 1 if i >= k: win[ord(s[i - k]) - 97] -= 1 if i >= k - 1 and ok(win): return True return False§6
易错点
✗ 错误写法:窗口长度没固定就比较
✓ 正确写法:先保证窗口长度等于 len(s1)
长度不同的窗口不可能是排列
✗ 错误写法:只判断字符是否出现
✓ 正确写法:必须比较出现次数
s1="aab" 时一个 a 不够
§
面试追问把动画讲成自己的话
追问核心思路是什么?
定长滑动窗口(长度=len(s1)),维护窗口内 26 字母频次;每滑一格「加右删左」,频次数组与 s1 的相等就说明窗口是 s1 的一个排列。
追问每步比较 26 个数会慢吗?
O(26)=O(1) 常数;也可维护一个 matched(已匹配字母数),加减时增量更新,matched 到 26 即命中,把比较降到真 O(1)。
追问复杂度多少?
窗口滑过 s2 一遍 O(n),每步比较 O(26)=O(1) → 总 O(n);两个 26 大小数组 O(1) 空间。
这道题到这就讲完了。动画和文字是同一套思路——别光看,关掉页面自己默写一遍。然后顺着主线继续:
下一题 · 滑动窗口 5/7
→最小覆盖子串
LeetCode 76 · 困难 · 沿着 滑动窗口 继续往下推进
把这道题真正学会,再走
图解算法年卡 ¥99 /年
- ✓本题每步动画的吴师兄语音讲解(全站陆续覆盖)
- ✓小欧带 8 步通关训练——追问到不看答案也能写对、能 30 秒讲给面试官听
- ✓学习报告,记录每道题的掌握程度
299道动画图解
37节语音讲解
28课数据结构
¥0.27折合 / 天
76k+ GitHub Star · 吴师兄开源图解算法,几十万开发者在看的算法讲解
¥99 开通年卡 →
想成体系刷透这类套路?去图解算法专题