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

1,214 阅读1分钟

image.png

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第29天,点击查看活动详情

测试岗位也越来卷了,除了基本的功能测试外,还要有编程基础、脚本经验才脱颖而出。

怎么才能提高我们的编程能力呢,刷LeetCode是最佳的途径之一,话不多数,刷题走起~

一、题目描述:

  • 题目内容

    image.png

  • 题目示例

    image.png

  • 题目解析

    • 1 <= s.length, p.length <= 3 * 104
    • s 和 p 仅包含小写字母

二、思路分析:

我们拿到本题,读取题意要求在字符串中给出字符串P的异位词,并返回在字符串中起始索引列表。看完题目,我们对题目中的一些细节点需要明确的:

  • 异位词:由相同字母组成的字符串。如"abc"与"cba"是异位词,因为它们都是由a,b,c三个字符组成的
  • 字符串s与字符串p都是由小写字母组成的
  • 字符串s中要找出字符串p的异位词,那么字符串s的长度要大于字符串p的,才有可能有异位词

以上明确了题目的要求,对于本题解答时,我们很容易想到了用滑动窗口方法来实现

  • 以字符串p长度的滑动窗口,在字符串s中中进行遍历查找

那么怎么判断滑动窗口中字符串与字符串p是异位词,如果只通过判断s[i]在字符串p中存在,那么对于p字符串重复字符就没有办法筛选出来了。

这个时候,如383. 赎金信中构建26字母初始列表来存储每一个字符出现的次数的方法来辅助我们s字符串滑动窗口中的字符次数

因此,实现的思路就比较清晰了,大致如下:

  • 初始化两个26个字母的列表次数默认为0,pcount和scount
  • p字符串和s字符串都是由小写字母组成,可以使用ord()-97来得到26字母列表的索引位置
  • 循环遍历p字符串,pcount记录每一个字符出现的次数
  • 遍历以p字符串长度遍历s字符串,滑动串口中的字符次数更新在scount,与pcount进行对比
  • 如果scount与pcount相等,则ans添加索引i到结果中 流程图 (2).jpg

按照这个思路,滑动串口中使用for循环来计算次数,代码很轻松的写出来了,但是超出时间限制通过不了

image.png

思考一番,滑动窗口中的字符串可以通过i和i+len(p)索引来更新scount.

  • 先求出当i=0时的scount字符串次数,并判断scount与pcount,如果相等则ans添加索引0
  • 当遍历s字符串中字符时,s[i]的次数被更新为0,添加一个s[i+len(p)]字符的次数
  • 同理判断scount与pcount相等,则ans添加i+1 根据上述优化思路,实现的Python代码如下:
class Solution(object):
    def findAnagrams(self, s, p):
        pcount = [0]*26
        scount = [0]*26
        ans = []

        if len(s) < len(p):
            return ans
        
        for i in range(len(p)):
            pcount[ord(p[i])-97] +=1
            scount[ord(s[i])-97] +=1
        
        if scount == pcount:
            ans.append(0)

        for i in range(len(s)-len(p)):
            scount[ord(s[i])-97] -=1
            scount[ord(s[i+len(p)])-97] +=1
            print(scount)
            if scount == pcount:
                ans.append(i+1)
        return ans 

上述遍历使用一个for循环+固定滑动窗口的思想,可以通过本题,但是效率还是比较慢的,我们继续思考优化:

  • 使用可变大小的滑动窗口,创建两个指针left和right,都指向s字符串起始位置
  • right指针开始移动,每一次移到scount更新所指的字符的次数
  • 当right-left+1与len(p)相等且pcount与scount相等,则ans添加left
  • 当right-left+1大于len(p)时,s[left]字符次数进行减1,left指针向右+1
  • 当right指针大于len(s),则退出循环
class Solution(object):
    def findAnagrams(self, s, p):
        """
        :type s: str
        :type p: str
        :rtype: List[int]
        """
        pcount = [0]*26
        scount = [0]*26
        ans = []
        left = 0
        right = 0

        if len(s) < len(p):
            return ans
        
        for i in range(len(p)):
            pcount[ord(p[i])-97] +=1

        while right < len(s):
            scount[ord(s[right])-97] +=1
            if right-left+1 > len(p):
                scount[ord(s[left])-97] -=1
                left +=1
            if right -left +1 == len(p) and scount == pcount:
                ans.append(left)
            right +=1
        return ans 

三、总结:

本题考察使用滑动串口思想遍历字符串,字符次数记录使用26个字母初始列表来进行记录,AC提交记录如下:

image.png

  • 时间复杂度O(n+m+s),n为s字符串长度,m为p字符串长度,s为26字母长度
  • 空间复杂度O(s),s为26个字母长度列表

以上是本期内容,欢迎大佬们点赞评论,下期见~~