持续创作,加速成长!这是我参与「掘金日新计划 · 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;
}
复制代码
解题思路
方法一中,每次遍历都要对每截字符串进行 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;
};
复制代码