在问 · 算法题解

438. 找到字符串中所有字母异位词

中等滑动窗口滑动窗口 + 计数数组比较力扣原题 ↗

题目描述

输入字符串 s 和 p。

如果 s 的某个子串与 p 长度相同,并且每种字母出现的次数都和 p 一样(即两者是字母异位词),就记下这个子串在 s 中的起始下标。

返回所有这样的起始下标,下标从 0 开始,顺序不限。没有符合条件的子串时返回空数组。

示例 1
输入:s = "cbaebabacd", p = "abc"
输出:[0,6]

解释:下标 0 处的 "cba" 和下标 6 处的 "bac" 都是 "abc" 的异位词。

示例 2
输入:s = "abab", p = "ab"
输出:[0,1,2]

解释:下标 0、1、2 开始的长度为 2 的子串分别是 "ab"、"ba"、"ab",都符合。

数据范围

  • 1 <= s.length, p.length <= 3 * 10^4
  • s 和 p 只含小写英文字母。

解题思路:滑动窗口 + 计数数组比较

维护长度为 len(p) 的固定窗口,用 26 个字符计数。右端加入字符,窗口超长时移除左端;计数相等时记录起点。

为什么正确

每次比较 win==need 前,win 表示当前窗口的字符频次,窗口长度为 min(i+1,k);当 i>=k-1 时长度恰为 k,此前因总字符数不足不会命中。

复杂度分析

时间 O(|s|+|p|)(26 个小写英文字母);辅助空间 O(26)=O(1),不计输出;若有 m 个命中起点,含输出空间 O(26+m)。

易错点

窗口长度必须固定为 len(p),先加入再移除旧字符时索引要对应 i-k。

参考代码

Python、Java、C++、Go、JavaScript、TypeScript 六种语言,逻辑一致。

class Solution:
    def findAnagrams(self, s,p):
        if len(p)>len(s): return []
        need=[0]*26; win=[0]*26; ans=[]; k=len(p)
        for ch in p: need[ord(ch)-97]+=1
        for i,ch in enumerate(s):
            win[ord(ch)-97]+=1
            if i>=k: win[ord(s[i-k])-97]-=1
            if win==need: ans.append(i-k+1)
        return ans

载入编辑器,自己跑一遍 →

其他解法

主解法每次比较两个计数数组,写法最简单;差异计数把比较的开销降为 O(1),字符集很大时优势明显。

解法 2:优化的滑动窗口(差异计数) 时间 O(|s| + |p| + Σ),空间 O(Σ)

count[c] 记录窗口中字母 c 的个数减去 p 中的个数,differ 记录 count 中非零的字母种数。窗口每移动一格只改两个字母,按“改之前为 0 则 differ 加 1,改之后为 0 则 differ 减 1”维护。differ 为 0 时窗口就是异位词。

每次移动 O(1),不用每次比较整个 26 长度的数组。需要维护 differ,边界更容易写错。

class Solution:
    def findAnagrams(self, s, p):
        n, k = len(s), len(p)
        if k > n:
            return []
        # count[c] = 窗口中 c 的个数 - p 中 c 的个数;differ = count 中非零的字母数
        count = [0] * 26
        for i in range(k):
            count[ord(s[i]) - 97] += 1
            count[ord(p[i]) - 97] -= 1
        differ = sum(1 for c in count if c != 0)
        ans = [0] if differ == 0 else []

        def change(c, d):
            nonlocal differ
            if count[c] == 0:
                differ += 1
            count[c] += d
            if count[c] == 0:
                differ -= 1

        for i in range(k, n):
            change(ord(s[i - k]) - 97, -1)
            change(ord(s[i]) - 97, 1)
            if differ == 0:
                ans.append(i - k + 1)
        return ans

滑动窗口 专题的其他题目

查看 滑动窗口 全部题目 →