Python算法训练序列
双指针与滑动窗口 · Unit 06
窗口长度固定,
每次只更新进入和离开的字符。
字母异位词不要求字符顺序相同,只要求字符频次完全一致。由于候选子串长度必须等于 len(p),窗口每次右移一格时,只需加入一个新字符并移除一个旧字符。
题目要求
给定字符串 s 和 p,找到 s 中所有是 p 的字母异位词的子串,并返回这些子串的起始下标。答案顺序不限。
约束
1 ≤ len(s), len(p) ≤ 30000s和p只包含小写英文字母。
示例
输出:[0,6]
"cba"与"bac"都是"abc"的异位词
输出:[0,1,2]
核心条件
候选窗口长度必须等于 len(p),且其中每个字符的出现次数与 p 完全一致。
窗口中不能出现重复字符。right 扩张;约束被破坏时,left 持续收缩,窗口长度会变化。
候选长度由 len(p) 预先确定。窗口每次整体右移一格,只更新一个进入字符和一个离开字符。
目标频次 target_count
窗口频次 window_count
当前循环不变量
进入匹配判断时,窗口长度恰好为 len(p),且window_count准确描述窗口频次。
当前移动规则
right加入新字符;窗口过长时移除s[left]并右移left;长度正确时比较两张频次表。
若每个长度为m的子串都重新构造频次表,最坏需要检查约n个窗口,每个窗口又统计m个字符,复杂度约为O(nm)。相邻窗口实际上只有一个字符离开、一个字符进入。
迁移题:LeetCode #567 字符串的排列
先认识新题,再判断哪些窗口状态可以直接复用。学习者尚未理解 #567 的输入、输出和示例之前,不进入代码迁移。
题目要求
给定两个字符串 s1 和 s2,判断 s2 是否包含 s1 的某一种排列。换句话说,只要 s2 中存在一个连续子串,其字符频次与 s1 完全一致,就返回 True;否则返回 False。
约束
1 ≤ len(s1), len(s2) ≤ 10⁴s1和s2只包含小写英文字母。
官方示例
输出:True
解释:连续子串 "ba" 是 "ab" 的一种排列。
输出:False
解释:不存在长度为2且包含一个a、一个b的连续子串。
返回目标
本题只询问“是否存在”,不要求返回匹配子串、起点下标或所有匹配位置。
读题检查 1|函数最终返回什么?
读题检查 2|为什么 s1="ab"、s2="eidbaooo" 返回 True?
已经掌握:#438 找到所有字母异位词
准备迁移:#567 字符串的排列
下面哪句话最准确地描述了迁移幅度?
槽位 A|什么情况可以在扫描前直接返回 False?
槽位 B|目标频次应该由谁建立?
槽位 C|什么时候当前窗口代表 s1 的一种排列?
槽位 D|匹配时与扫描结束时分别返回什么?
result.append(left) 改成 return True,并在循环结束后返回 False。#438:收集所有匹配起点
target_count = Counter(p)
window_count = Counter()
result = []
...
if window_count == target_count:
result.append(left)
return result#567:判断是否存在排列
target_count = Counter(s1)
window_count = Counter()
...
if window_count == target_count:
return True
return False可选挑战:脱离槽位,独立重写完整代码
这一步不阻碍主流程。固定窗口和 Counter 骨架已经保留,只需要补回匹配返回和最终返回。
本单元形成的算法Skill
当候选连续区间长度固定、判断条件依赖元素组成时,使用固定长度滑动窗口。每次右移只更新两个差异:新字符进入、旧字符离开;窗口长度正确后再比较状态,而不是重新统计整段。