手摸手提桶跑路——LeetCode438. 找到字符串中所有字母异位词

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

题目描述

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

解题思路

字母异位词的意思就是长度相等,且各个字母的数量相同的字符串。

一个朴素的方法是,将字符串 p 的各个字母计数,然后遍历字符串 s ,每次从 i 的位置开始,查看 s[i]、s[i + 1]...s[i + p.length] 这截字符串是不是字符串 p 的字母异位词,如果是的话,就将这个字母异位词的起始索引 i 添加到结果数组中。

那么应该使用什么方式来统计呢?

一开始我们是使用对象来对每截字符串进行计数,然而这样的效率太慢了,会超时。

因此后面使用数组映射的方式。初始化一个长度为 26 的数组,填充 0,一个索引代表的是一个字母(根据ASCII码),比如字母 'a' 对应的索引就是 'a'.charAtCode() - 'a'.charAtCode() , 也就是 0 ,以此类推。

那么当 p 映射完之后,我们便得到了 p 的映射数组,比如 p=aab ,那么映射数组为 [2,1,0,0,....0] 21(24个0)。

后续遍历每截字符串时,都进行一次映射。如果是字母异位词的话,两个数组 对应索引的值是一样的 ,这里可以简单的用 toString() 方法去比较两个数组转为字符串之后的值是否相等来判断。

题解

/**
 * @param {string} s
 * @param {string} p
 * @return {number[]}
 */
var findAnagrams = function(s, p) {
    const ans = [], lens = p.length, mp = trans(p);

    for(let i=0, l = s.length; i<l; ++i) {
        if(trans(s.substr(i, lens).split('')).toString() === mp.toString()) {
            ans.push(i);
        } 
    }
    return ans;
};

const trans = (p) => {
    const mp = new Array(26).fill(0);
    for(const w of p) {
        mp[w.charCodeAt() - 'a'.charCodeAt()]++;
    }
    return mp;
}
复制代码

捕获.PNG

解题思路

方法一中,每次遍历都要对每截字符串进行 s.substr(i, lens).split('') 来比对字母数,特别耗时。实际上我们在遍历的时候,可以用 减法 来做。

还是这个概念:如果两个字符串是字母异位词,那么其 数量及长度必然是相等的

具体步骤如下:

  1. 对字符串 p 进行字母的数组映射,映射后的数组为 count
  2. 遍历字符串 s ,初始化一个 left 指针和一个 right 指针,表示每截字符串的开始和结束。
  3. 我们让 count[s[right].chatCodeAt()-'a.charCodeAt()']1,表示 抵消 一个字母。如果出现负数说明不是异位词,需要重置 count 回原来的样子。如果是字母异位词,减到最后必然有 count 所有下标对应值 都为0 , 且 right - left - 1 === n ,将起始下标 left 加入到结果数组中。
  4. 重复步骤3

题解

/**
 * @param {string} s
 * @param {string} p
 * @return {number[]}
 */
var findAnagrams = function(s, p) {
    const ans = [], count = new Array(26).fill(0), m = s.length, n = p.length;
    for(const w of p) {
        ++count[w.charCodeAt() - 'a'.charCodeAt()];
    }
    for(let l=0, r=0; r<m; ++r) {
        --count[s[r].charCodeAt() - 'a'.charCodeAt()];
        while(count[s[r].charCodeAt() - 'a'.charCodeAt()] < 0) {
            ++count[s[l].charCodeAt() - 'a'.charCodeAt()];
            l++;
        }
        if(r - l + 1 === n) {
            ans.push(l);
        }
    }
    return ans;
};
复制代码

捕获.PNG

猜你喜欢

转载自juejin.im/post/7150320640292028446