算法刷题自记录 | Leetcode438. 找到字符串中所有字母异位词(滑动窗口)

题目

题目描述:给定两个字符串 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 * 104
s 和 p 仅包含小写字母

思路

  本质是滑动窗口问题。窗口大小即为字符串 的大小,然后从左至右遍历 s 字符串,每次比较当前滑动窗口是否与 p 一样,如果一样则将当前滑动窗口第一个字符串在 s 中的位置添加至返回的列表中。

  这题的重点就是如何比较滑动窗口与 p 是否一样。这里我尝试了两个方法:

        1. 因为我是用的python,第一时间就想到了直接将滑动窗口和 p 转换为list,然后比较 sorted(list(滑动窗口)) == sorted(list(p))。这是比较容易理解的一个方法,不过可能是因为循环的每一步都用了sorted(),所以提交之后发现时间较慢。

        2. 看了几个高赞题解,理解了之后试了第二个方法,时间确实快一些。这个方法声明了两个长度为26(26个字母)的全0列表,然后将滑动窗口和 p 中的字符出现的频次记录在这两个列表中。最后直接比较这两个列表是否相同。其中滑动窗口每向右滑动一个元素,则将该元素在 s 对应的列表中的频次减1,代表该元素移出滑动窗口。接着再将新挪进滑动窗口的元素频次加1。

Python3代码

方法一:(利用sorted()函数)

class Solution:
    def findAnagrams(self, s: str, p: str) -> List[int]:
        result = []
        l = 0
        p_li = sorted(list(p))
        while l <= len(s) - len(p):         # if s 剩下的字符串长度 < p 字符串长度,就不需要比较了,因为肯定不是子串
            s_li = sorted(s[l: l + len(p)]) # 当前滑动窗口
            if s_li == p_li:                # 比较当前窗口是否等于 p
                result.append(l)
            l += 1
        return result

 方法二:(时间更快)

  • 时间复杂度:O(N)。其中N为字符串 s 的长度。
  • 空间复杂度:O(1)
class Solution:
    def findAnagrams(self, s: str, p: str) -> List[int]:
        n, m = len(s), len(p)
        s_li = [0] * 26
        p_li = [0] * 26
        result = []

        if n < m:
            return result
            
        for i in range(m):
            s_li[ord(s[i]) - 97] += 1         # 97 = ord('a')
            p_li[ord(p[i]) - 97] += 1
        if s_li == p_li:
            result.append(0)
        
        for i in range(m, n):
            s_li[ord(s[i - m]) - 97] -= 1     # 将滑动窗口的第一个字符去掉(滑动窗口向右滑动一位)
            s_li[ord(s[i]) - 97] += 1         # 添加滑动后的滑动窗口中最右边的字符
            if s_li == p_li:
                result.append(i - m + 1)      # 将滑动窗口的第一个字符所在位置添加进result中

        return result
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值