【Leetcode】找到字符串中所有字母异位词

1 题目

给定两个字符串 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" 的异位词。

2 分析

这道题目第一反应是对p进行排序,只要每个s的子串排序后与p_sorted相等,即表明它是p的字母异位词。因此,最简单的方法就是获取p的长度n,然后遍历s的每个位置作为起始位置,取sorted(si:i+n)与p_sorted比较。此种方案虽然能跑通,但超时了。

python 复制代码
def findAnagrams(s, p):
    p_list = sorted(p)
    n = len(p)
    m = len(s)
    res = []
    for i in range(m):
        if i+n<m:
            s_list = sorted(s[i:i+n+1])
            if s_list==p_list:
                res.append(i)
    return res

上面的方案其实有很多重复工作,因为每比较一次位置i,其实比较了其之后n个位置的字母组合是否是p_sorted的异位词。如果能够将前面比较的结果记录下来,每次i右移的时候只比较增加和减少的字母,那么就能减少很多多余的工作。因此问题转变成了什么样的数据结构能够通过单个字母的比较实现异位词的判断。其实异位词除了可以用排序后的列表比较以外,还可以用字母出现次数的字典表示。如果每个字母出现的次数都相等,意味着它们是字母异位词。

python 复制代码
def findAnagrams(s, p):
    n = len(p)
    p_dict = {}
    for char in p:
        if char not in p_dict:
            p_dict[char] = 1
        else:
            p_dict[char] + 1
    if len(s) == 0:
        return []
    res = []
    s_dict = {}
    left = 0
    for i in range(len(s)):
        if s[i] not in s_dict:
            s_dict[s[i]] = 1
        else:
            s_dict[s[i]] += 1
        if i - left + 1> n:
            s_dict[s[left]] -= 1
            left += 1
            s_dict = {k: v for k,v in s_dict.items() if v>0}
        if s_dict == p_dict:
            res.append(left)
    return res

上面的方法虽然可以通过所有测试,但速度很慢(只打败5%的人)。主要原因在于为了做字典比较有一步遍历非常耗时:s_dict = {k: v for k,v in s_dict.items() if v>0}。另外_dict == p_dict的字典比较也是性能瓶颈。于是,自然而然想到将p_dict表示成由所有26个字母组成的字典,如果没有该字母,次数为0,这样后续就不用为了保证键值的一致遍历字典了。更进一步,直接用字母的ASCII值表示字母的位置,甚至都不用维护字典,直接比较列表即可。

python 复制代码
def findAnagrams(s, p):
    if len(p) > len(s):
        return []
    n = len(p)
    p_count = [0] * 26
    s_count = [0] * 26
    for char in p:
        p_count[ord(char)-97] += 1
    res = []
    left = 0
    for i in range(len(s)):
        s_count[ord(s[i])-97] += 1
        if i - left + 1> n:
            s_count[ord(s[left])-97] -= 1
            left += 1
        if s_count == p_count:
            res.append(left)
    return res

上面的方案还有一些点可以优化:

  • 当s_count的长度没有达到p_count的长度的时候其实不需要比较
  • 在填充p_count的时候可以同时填充s_count,然后直接从s_count比p_count长的地方开始做填充。

3 代码

python 复制代码
def findAnagrams(s, p):
    """
    :type s: str
    :type p: str
    :rtype: List[int]
    """
    if len(p) > len(s):
        return []
    n = len(p)
    m = len(s)
    p_count = [0] * 26
    s_count = [0] * 26
    for i in range(n):
        p_count[ord(p[i])-97] += 1
        s_count[ord(s[i])-97] += 1
    res = []
    if s_count == p_count:
        res.append(0)
    for i in range(m-n):
        s_count[ord(s[i+n])-97] += 1
        s_count[ord(s[i])-97] -= 1
        if s_count == p_count:
            res.append(i+1)
    return res
相关推荐
旖旎夜光1 分钟前
LeetCode 904:水果成篮(滑动窗口) —— 题解
数据结构·c++·算法·leetcode·滑动窗口
怪奇云呼军22 分钟前
G.711、Opus 和重采样会拖慢识别吗?闪电智能VoiceAgent 的音频入口怎么选
java·人工智能·python·算法·云计算·音视频
ZC跨境爬虫35 分钟前
LeetCode 27. 移除元素(双指针详解 + Java Python 多解法对比)
java·python·leetcode
乐观勇敢坚强的老彭2 小时前
C++ 竞赛常用算法模板速查表
开发语言·c++·算法
Dr.kangder2 小时前
嵌入式面试总结(十六)——AMBA总线
面试·职场和发展·架构·嵌入式·虚拟化·总线
高洁012 小时前
工信部教考中心证书
人工智能·深度学习·算法·机器学习·知识图谱
我变成萤火虫2 小时前
河南萌新联赛2026第(三)场:郑州轻工业大学
数据结构·c++·算法·贪心算法·stl·深度优先·哈希算法
Dr.kangder2 小时前
嵌入式面试总结(十九)——内存泄露
单片机·算法·面试·职场和发展·架构·硬件架构
Dr.kangder2 小时前
嵌入式面试总结(十七)——DMA总线
面试·职场和发展·架构·嵌入式·总线
Herbert_hwt2 小时前
【C语言基础】常量、选择结构与运算符全解析
c语言·开发语言·算法