题目描述
给定两个字符串 s 和 p,找到 s 中所有 p 的异位词的子串,返回这些子串的起始索引。不考虑答案输出的顺序。
示例 1:
输入: s = "cbaebabacd", p = "abc" 输出: [0,6] 解释: 起始索引等于 0 的子串是 "cba", 它是 "abc" 的异位词。起始索引等于 6 的子串是 "bac", 它是 "abc" 的异位词。
示例 2:
输入: s = "abab", p = "ab" 输出: [0,1,2] 解释: 起始索引等于 0 的子串是 "ab", 它是 "ab" 的异位词。起始索引等于 1 的子串是 "ba", 它是 "ab" 的异位词。起始索引等于 2 的子串是 "ab", 它是 "ab" 的异位词。
提示:
1 <= s.length, p.length <= 3 * 10^4s和p仅包含小写字母
核心方法:固定长度滑动窗口 + 数组计数抵消
核心逻辑梳理
- 异位词的本质:子串与
p的字符种类完全相同、每种字符的出现次数完全相同(顺序无关),因此子串长度必须等于p的长度(这是前提,窗口长度固定为len(p))。 - 计数抵消的核心:
- 先统计
p中所有字符的出现次数,将其存入计数数组并设为「负值」(相当于「需要抵消的目标」)。 - 再用滑动窗口遍历
s,窗口内的字符计数「加 1」,当计数数组所有值都归 0 时,说明窗口内字符与p完全匹配(异位词)。
- 先统计
- 固定窗口滑动规则:
- 初始化第一个窗口(
s的前len(p)个字符),完成初始计数抵消。 - 后续窗口整体向右滑动,移除窗口左侧离开的字符(计数减 1)、添加窗口右侧新增的字符(计数加 1),无需重新统计整个窗口,保证效率。
- 初始化第一个窗口(
- 优化点:用长度为 26 的数组(对应 26 个小写英文字母)替代字典,避免
KeyError,且数组访问 / 比较效率更高,是本题的最优选择(时间复杂度 O(n),空间复杂度 O(1))。 - 边界处理:若
s长度小于p长度,直接返回空列表(无有效子串)。
思路落地关键补充
- 判断全 0 时记录 left 索引完全正确,数组格式下直接用
count == [0] * 26即可快速判断。 - 遇到不属于
p的字符时,无需额外跳转(数组天然只统计 26 个字母,且窗口固定长度,滑动时会自动清除无效字符的计数影响),简化逻辑。
from typing import List
class Solution:
def findAnagrams() -> []:
result = []
s_len = (s)
p_len = (p)
s_len < p_len:
result
count = [] *
c p:
idx = (c) - ()
count[idx] -=
i (p_len):
idx = (s[i]) - ()
count[idx] +=
i (s_len - p_len + ):
count == [] * :
result.append(i)
i == s_len - p_len:
left_char_idx = (s[i]) - ()
count[left_char_idx] -=
right_char_idx = (s[i + p_len]) - ()
count[right_char_idx] +=
result

