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

目录

题目描述

示例1:

示例2:

提示:

解题思路

Collections库

介绍

滑动窗口法

概念

应用场景及特点:

思路

流程展示

代码

复杂度分析


题目描述

给定两个字符串sp,找到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**4)
  • sp 仅包含小写字母

解题思路

Collections库

介绍

Python的collections库是一个内建模块,它提供了一系列特殊的容器数据类型,用于扩展Python的标准内建容器(如字典、列表、集合和元组)。这些特殊的容器类型提供了比通用数据类型更多的选择和更好的性能,非常适合在需要高效数据处理和复杂数据结构时使用。

滑动窗口法

概念

滑动窗口是一个在序列上移动的区间,通常由左右两个指针来界定这个区间的范围。通过移动指针来改变窗口的大小和位置,在窗口移动的过程中,根据问题的需求进行特定的计算和处理。

应用场景及特点

  1. 子数组 / 子串问题
  • 当需要在一个序列中找到满足特定条件的连续子数组或子串时,滑动窗口非常适用。例如,寻找和为特定值的连续子数组、含有特定字符的最长子串等。
  • 窗口的大小通常是动态变化的,根据问题的条件进行调整。
  1. 高效性
  • 相比于暴力枚举所有可能的子数组 / 子串,滑动窗口法通常能够在更短的时间内找到解。因为它利用了子数组 / 子串的连续性和窗口的滑动特性,避免了重复计算。
  1. 指针移动规则
  • 通常有两个指针,一个指向窗口的左端,一个指向窗口的右端。根据问题的具体要求,以特定的方式移动指针。
  • 例如,在寻找满足特定条件的最小子数组时,可能会先扩大窗口直到满足条件,然后再缩小窗口以找到最小的满足条件的窗口。

思路

这道题可以使用滑动窗口(Sliding Window)和哈希表的结合来解决。解题的关键是如何有效地在字符串 s 中找到与字符串 p 的异位词相同的子串。

  1. 异位词特征:两个字符串是异位词,那么它们每个字符的出现次数是相同的。因此,我们可以使用字符频率来判断一个子串是否是异位词。
  2. 滑动窗口 :我们在字符串 s 上维护一个大小为 len(p) 的滑动窗口,记录窗口内的字符频率。当窗口的大小等于 p 的长度时,我们检查窗口内的字符频率是否和 p 的字符频率相同。如果相同,则说明当前窗口是 p 的一个异位词,我们记录下窗口的起始索引。
  3. 窗口滑动:每次窗口滑动时,我们更新窗口的字符频率,移出窗口左边的一个字符,并添加右边新进入窗口的字符。

流程展示

代码

python 复制代码
class Solution:  
    def findAnagrams(self, s: str, p: str) -> List[int]:  
        res = []
        n, m = len(s), len(p)
        if n < m:
            return res
        
        # 初始化p的字符频率
        p_count = collections.Counter(p)
        # 初始化窗口的字符频率
        window = collections.Counter(s[:m-1])
        
        for i in range(m-1, n):
            # 增加新的字符到窗口
            window[s[i]] += 1
            # 如果窗口字符频率和p字符频率相同,记录起始索引
            if window == p_count:
                res.append(i - m + 1)
            # 移除左边的字符,保持窗口大小为m
            window[s[i - m + 1]] -= 1
            if window[s[i - m + 1]] == 0:
                del window[s[i - m + 1]]
        
        return res

复杂度分析

  • 时间复杂度O(n),其中 n 是字符串 s 的长度。遍历字符串 s 需要 O(n) 时间,在每次窗口移动时,我们对窗口内的字符进行更新和比较,哈希表的比较是 O(1),因此总时间复杂度是 O(n)
  • 空间复杂度O(m),其中 m 是字符串 p 的长度。我们使用了两个哈希表来存储字符频率,每个哈希表最多存储 m 个不同的字符,因此空间复杂度是 O(m)
相关推荐
qingyulee15 分钟前
线性回归、决策树
人工智能·算法·线性回归
Zhang~Ling23 分钟前
C++ 继承机制详解下:多继承、虚继承与菱形继承底层原理
开发语言·c++·算法
西安邮电大学24 分钟前
2026华为OD机考真题附答案-计算数列位置N的值
java·算法
AI算法沐枫33 分钟前
机器学习经典小项目4:泰坦尼克号生存预测
人工智能·python·深度学习·线性代数·算法·机器学习·回归
洛水水33 分钟前
【力扣100题】60.缺失的第一个正数
leetcode·哈希算法
玖釉-38 分钟前
单词搜索:二维网格中的 DFS 回溯与剪枝优化
c++·windows·算法·深度优先·剪枝
吴可可12341 分钟前
C++与C#版Teigha样条离散化差异解析
c++·算法·c#
KaMeidebaby1 小时前
卡梅德生物技术快报|抗体的制备与纯化:分子实验实操:番茄 sHSP 重组表达与抗体的制备与纯化工艺
前端·数据库·人工智能·其他·算法·百度·新浪微博
Han.miracle1 小时前
Java HashMap 与 ConcurrentHashMap 核心原理总结:从 Hash 冲突到 LongAdder
java·算法·哈希算法
菜菜的顾清寒1 小时前
力扣HOT100(35)回溯-全排列
算法·leetcode·职场和发展