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

1:题目描述(力扣

给定两个字符串 s 和 p,找到 s 中所有 p 的 异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。

异位词 指由相同字母重排列形成的字符串(包括相同的字符串)。

2:解题思路

因为字符串s和字符串p中的都仅包含小写字母,小写字母的数量是有限的,为26个,因此我们考虑使用哈希结构-数组来解决该题。

第一种解法:滑动窗口+数组

思路:

        求的是p在s中的子串,所以可以统计出字符串p中的字母数量,然后遍历去统计字符串s中符合长度的子串中字母的数量, 判断子串中字母数量是否与p中字母数量一致,一致则将子串的起始位置加入列表中,不一致,则向后移动子串的起始位置,此时需要减去上一个子串的起始位置的字母数量。依次循环,直到最后一个符合长度的子串。

具体步骤如下:

第一步:定义一个列表res=[],用来存放满足条件的子串起始位置,初始化为空数组;定义一个变量s_len=len(s),定义一个变量p_len=len(p)。

第二步:先判断字符串s的长度是否小于字符串p,小于则s不可能包含有p的字母异位词的子串,直接返回res。

第三步:定义一个数组s_count,长度为26,初始值均为0,用来存储s中子串的字母出现数量;

定义一个数组p_count,长度为26,初始值均为0,用来存储p中字母出现的数量。

第四步:使用for循环遍历字符串p和s中从0开始长度为p_len的子串,统计p中字母出现的数量,统计子串中字母出现的数量

以示例一为例:

第五步:判断数组s_count和数组p_count内的元素是否相等,相等,则将子串的起始位置下标0加入到res中。

第六步:使用for循环遍历字符串s,以子串的结束位置下标为循环条件,从第一个子串的结束位置(即字符串p的长度)开始,到字符串s的长度s_len结束;因为子串的起始位置已经向后移动一位了,所以我们需要在s_count中,减去前一个子串的起始位置字母的数量,再加上现在子串的结束位置的字母数量;然后比较s_count和p_count中的元素是否一致,一致则将子串的起始位置加入res,不一致则进行下一次循环。

第七步:循环字符串s结束后,返回res。

代码展示:

class Solution:
    def findAnagrams(self, s, p):
        s_len, p_len = len(s), len(p)
        # 当字符串s的长度小于字符串p的长度,s中不可能包含p中的子串,返回空数组
        if s_len < p_len:
            return []

        res = []
        s_count = [0] * 26
        p_count = [0] * 26
        # 先将p字符串中包含的字母数量统计出来,并且同时统计字符串s中,从下标0开始,与p长度相同的子串中字母的数量
        for i in range(p_len):
            s_count[ord(s[i]) - ord("a")] += 1
            p_count[ord(p[i]) - ord("a")] += 1
        # 判断两个数组是否相等,相等则将下标0加入到res中
        if s_count == p_count:
            res.append(0)
        # 以长度为p_len为准,每次向后一位移动,i表示最后一个字符的位置
        for i in range(p_len, s_len):
            # 向后滑动一位,需要前面的字母对应的数量,减掉
            s_count[ord(s[i-p_len]) - ord("a")] -= 1
            # 滑动后,需要把最后一个字符串的数量加上
            s_count[ord(s[i]) - ord("a")] += 1
            # 判断两个数组是否相等
            if s_count == p_count:
                # 相等则将该子串的起始位置下标加入res,子串起始下标为i-p_len+1
                res.append(i-p_len+1)
        return res

第二种解法:滑动窗口+双指针+数组

用双指针来表示滑动窗口的两侧边界,当滑动窗口的长度等于p的长度时,表示找到一个异位词。

思路:

        定义滑动窗口的左右两个指针left,right
        right一步一步向右走遍历s字符串
        right当前遍历到的字符加入s_count后不满足p_count的字符数量要求,将滑动窗口左侧字符不断弹出,也就是left不断右移,直到符合要求为止。
        当滑动窗口的长度等于p的长度时,这时的s子字符串就是p的异位词。
        其中,left和right表示滑动窗口在字符串s中的索引,s_left和s_right表示字符串s中索引为left和right的字符在数组中的索引

代码展示:

# 滑动窗口+双指针
class Solution:
    def findAnagrams(self, s, p):
        s_len, p_len, res = len(s), len(p), []
        # 当s的长度小于p时,返回空数组
        if s_len < p_len:
            return res

        p_count = [0] * 26
        s_count = [0] * 26
        # 统计p中字母的数量
        for i in range(p_len):
            p_count[ord(p[i]) - ord("a")] += 1
        # left为左指针,表示子串的起始位置,初始值为0
        left = 0
        # right为右指针,表示子串的结束位置
        for right in range(s_len):
            # 统计子串最后一个字母的数量
            s_right = ord(s[right]) - ord("a")
            s_count[s_right] += 1
            # 当遍历到的字母索引的数量大于p中对应索引的数量时,说明包含这个字母的子串都不满足要求,则需要将左边的元素一个一个的弹出
            while s_count[s_right] > p_count[s_right]:
                s_left = ord(s[left]) - ord("a")
                s_count[s_left] -= 1
                # 每减去左指针对应的字母索引的数量,需要向后移动一位
                left += 1
            # 当right与left包含的字母长度等于p的长度时,为满足要求的子串,将子串的起始位置left加入res
            if right-left+1 == p_len:
                res.append(left)
        return res
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值