LeetCode 算法:找到字符串中所有字母异位词

284 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第 15 天,点击查看活动详情

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

原题地址

给定两个字符串 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 *10410^4
  • s 和 p 仅包含小写字母

思路分析

方法一

  1. 题目中需要找出 sp 的所有异位词的下标结果集;
  2. 首先若 s.length < p.length,那么结果肯定为 false
  3. 遍历 s,以 i < s.length - p.length + 1 为终止条件,首先截取跟 p 长度相同的字符串,来判断是否跟 p 是异位词,若是,则将下标放入结果集 res 中;最后返回 res 即可;
  4. 那么,我们需要一个辅助函数来判断两个字符串是否为异位词,方法一使用的是先拆分成数组,然后排序后再组成字符串,比较两个字符串是否相同的方式。使用的是两个异位词排序后肯定是一样的思想。但是这种方法,在执行时超出时间限制了。

方法二

  1. 步骤1~4 同方法一,唯一不同的就是判断两个字符串是否为异位词的方法;
  2. 两个字符串是异位词意味着 两个字符串中字符出现的种类和次数均相等。根据题意可知字符串只包含 26 个小写字母,所以定义一个长度为 26 的数组 res,先遍历记录字符串 s,得到其中每个字符出现的次数,然后遍历字符串 t,减去 res 中对应的次数,若 res[i]<0,返回 false 即可。
  3. 使用方法二,执行测试用例时,未发生超时。

AC 代码

方法一

/**
 * @param {string} s
 * @param {string} p
 * @return {number[]}
 */
var findAnagrams = function(s, p) {
    if(s.length < p.length) return []
    const res = []
    const len = p.length
    for(let i = 0; i < s.length - len + 1; i++) {
        const str = s.substr(i, len)
        if(isWords(str, p)) {
            res.push(i)
        }
    }
    return res
};

var isWords = function(s, t) {
   return s.split('').sort().join('') === t.split('').sort().join('')
}

结果:

  • 执行结果: 超出时间限制

方法二

/**
 * @param {string} s
 * @param {string} p
 * @return {number[]}
 */
var findAnagrams = function(s, p) {
    if(s.length < p.length) return []
    const res = []
    const len = p.length
    for(let i = 0; i < s.length - len + 1; i++) {
        const str = s.substr(i, len)
        if(isWords(str, p)) {
            res.push(i)
        }
    }
    return res
};

var isWords = function(s, t) {
   const res = new Array(26).fill(0)
    for (let i = 0; i < s.length; ++i) {
        res[s.codePointAt(i) - 'a'.codePointAt(0)]++
    }
    for (let i = 0; i < t.length; ++i) {
        res[t.codePointAt(i) - 'a'.codePointAt(0)]--
        if (res[t.codePointAt(i) - 'a'.codePointAt(0)] < 0) {
            return false
        }
    }
    return true
}

结果:

  • 执行结果: 通过
  • 执行用时:3872 ms, 在所有 JavaScript 提交中击败了5.35%的用户
  • 内存消耗:48.2 MB, 在所有 JavaScript 提交中击败了44.12%的用户
  • 通过测试用例:61 / 61

END