持续创作,加速成长!这是我参与「掘金日新计划 · 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 * 104s和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;
}
解题思路
方法一中,每次遍历都要对每截字符串进行 s.substr(i, lens).split('') 来比对字母数,特别耗时。实际上我们在遍历的时候,可以用 减法 来做。
还是这个概念:如果两个字符串是字母异位词,那么其 数量及长度必然是相等的。
具体步骤如下:
- 对字符串
p进行字母的数组映射,映射后的数组为count。 - 遍历字符串
s,初始化一个left指针和一个right指针,表示每截字符串的开始和结束。 - 我们让
count[s[right].chatCodeAt()-'a.charCodeAt()']减1,表示 抵消 一个字母。如果出现负数说明不是异位词,需要重置count回原来的样子。如果是字母异位词,减到最后必然有count所有下标对应值 都为0 , 且right - left - 1 === n,将起始下标left加入到结果数组中。 - 重复步骤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;
};