LeetCode 75 —— 438. 找到字符串中所有字母异位词

81 阅读3分钟

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

LeetCode 75 —— 438. 找到字符串中所有字母异位词

一、题目描述:

给定两个字符串 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 * 10^4

s 和 p 仅包含小写字母

来源:力扣(LeetCode)

链接:leetcode.cn/problems/fi…

著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

二、思路分析:

  1. 这道题考察了什么思想?你的思路是什么?

    这道题目我的第一想法是建立一个map[byte]int将p的每个英文字母放入map中,根据英文字母及其数目存放,然后再遍历s,从当前坐标到之后的p长度个字符放入一个map,然后使用reflect包的DeepEqual()方法比较两个map是否相等。然后再将该map中的元素清空。这样时间复杂度太大了!需要用到多个for循环。

    将这个思路进行改良,我们就可以使用滑动窗口来解决此题。

    我们先将p放入一个int类型的切片,其位置代表在26个英文字母中的位置,其值代表其数目。同时放入s中p长度数目的字母,然后直接比较即可,如果相等,就说明可以将索引0放入。

    然后遍历s,这里注意需要是s的截取字符串,截取范围为[:sLen-pLen],然后每次将当前坐标去除,将截取的字符串末尾后一个放入,每次放入都与p的字符切片比较是否相等。

  2. 做题的时候是不是一次通过的,遇到了什么问题,需要注意什么细节?

    不是一次通过的,刚开始的思路有点问题,写到一半感觉会超时,于是才采用滑动窗口的方法。

  3. 有几种解法,哪种解法时间复杂度最低,哪种解法空间复杂度最低,最优解法是什么?其他人的题解是什么,谁的效率更好一些?用不同语言实现的话,哪个语言速度最快?

    下面有一种优化方法值得我们学习!

image.png

```
func findAnagrams(s, p string) (ans []int) {
    sLen, pLen := len(s), len(p)
    if sLen < pLen {
        return
    }
​
    count := [26]int{}
    for i, ch := range p {
        count[s[i]-'a']++
        count[ch-'a']--
    }
​
    differ := 0
    for _, c := range count {
        if c != 0 {
            differ++
        }
    }
    if differ == 0 {
        ans = append(ans, 0)
    }
​
    for i, ch := range s[:sLen-pLen] {
        if count[ch-'a'] == 1 { // 窗口中字母 s[i] 的数量与字符串 p 中的数量从不同变得相同
            differ--
        } else if count[ch-'a'] == 0 { // 窗口中字母 s[i] 的数量与字符串 p 中的数量从相同变得不同
            differ++
        }
        count[ch-'a']--
​
        if count[s[i+pLen]-'a'] == -1 { // 窗口中字母 s[i+pLen] 的数量与字符串 p 中的数量从不同变得相同
            differ--
        } else if count[s[i+pLen]-'a'] == 0 { // 窗口中字母 s[i+pLen] 的数量与字符串 p 中的数量从相同变得不同
            differ++
        }
        count[s[i+pLen]-'a']++
​
        if differ == 0 {
            ans = append(ans, i+1)
        }
    }
    return
}
​
作者:LeetCode-Solution
链接:https://leetcode.cn/problems/find-all-anagrams-in-a-string/solution/zhao-dao-zi-fu-chuan-zhong-suo-you-zi-mu-xzin/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
```

三、AC 代码:

func findAnagrams(s, p string) (ans []int) {
    sLen, pLen := len(s), len(p)
    if sLen < pLen {
        return
    }
​
    var sCount, pCount [26]int
​
    for i, ch := range p {
        sCount[s[i] - 'a']++
        pCount[ch-'a']++
    }
​
    if sCount == pCount {
        ans = append(ans,0)
    }
​
    for i, ch := range s[:sLen-pLen]{
        sCount[ch-'a']--
        sCount[s[i+pLen]-'a']++
        if sCount == pCount {
            ans = append(ans, i+1)
        }
    }
    return 
}

image.png

image.png

四、总结:

时间复杂度为O(m+(n-m)26),空间复杂度为O(Σ),Σ为用于存储字符串 pp* 和滑动窗口中每种字母的数量。

优化后的方法时间复杂度为O(n+m+26),空间复杂度无变化。

模板来源:

作者:掘金酱

链接:juejin.cn/post/706970…

来源:稀土掘金

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。