给定两个字符串 s
和 p
,找到 s
中所有 p
的 异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。
示例 1:
makefile
输入: s = "cbaebabacd", p = "abc"
输出: [0,6]
解释:
起始索引等于 0 的子串是 "cba", 它是 "abc" 的异位词。
起始索引等于 6 的子串是 "bac", 它是 "abc" 的异位词。
示例 2:
makefile
输入: 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
仅包含小写字母
题解:滑动窗口
- 我们需要检查
s
中长度为p.length
的子串,是否是p
的异位词。 - 所谓异位词:两个字符串的字母出现次数完全一致。
- 所以我们可以用 频次数组/哈希表 来比较子串与
p
的字母频率。
滑动窗口
- 用两个数组
need
和window
统计字母出现次数。 - 窗口大小固定为
p.length
,右指针扩展窗口,左指针收缩窗口。 - 每次窗口长度等于
p.length
时,比较两个数组是否一致,如果一致就把left
加入结果。
比较两个数组是否相等可以通过记录"匹配的字符个数"来优化,而不是每次都遍历 26 个字母。
js
var findAnagrams = function(s, p) {
const sLen=s.length,pLen=p.length
const res=[]
if(sLen<pLen) return []
const sCount=new Array(26).fill(0)
const pCount=new Array(26).fill(0)
for(let i=0;i<pLen;i++){
sCount[s[i].charCodeAt()-'a'.charCodeAt()]++
pCount[p[i].charCodeAt()-'a'.charCOdeAt()]++
}
if(sCount.toString()===pCount.toString()){
res.push(0)
}
for(let i=0;i<sLen-pLen;i++){
sCount[s[i].charCodeAt()-'a'.charCodeAt()]--
sCount[s[i+pLen].charCodeAt()-'a'.charCodeAt()]++
if(sCount.toString()===pCount.toString()){
res.push(i+1)
}
}
return res
}
优化后的滑动窗口(有点难理解,记住上面的方法也行)
用一个数组 count
表示每个字母的差异:
count[i] > 0
:窗口里这个字母比p
多count[i] < 0
:窗口里这个字母比p
少count[i] === 0
:这个字母数量相同
用一个变量 differ
来记录 有多少个字母的数量不相等:
differ === 0
→ 当前窗口正好是p
的异位词
js
/**
* @param {string} s
* @param {string} p
* @return {number[]}
*/
var findAnagrams = function(s, p) {
const sLen = s.length, pLen = p.length;
if (sLen < pLen) {
return [];
}
const ans = [];
const count = Array(26).fill(0);
for (let i = 0; i < pLen; ++i) {
++count[s[i].charCodeAt() - 'a'.charCodeAt()];
--count[p[i].charCodeAt() - 'a'.charCodeAt()];
}
let differ = 0;
for (let j = 0; j < 26; ++j) {
if (count[j] !== 0) {
++differ;
}
}
if (differ === 0) {
ans.push(0);
}
for (let i = 0; i < sLen - pLen; ++i) {
if (count[s[i].charCodeAt() - 'a'.charCodeAt()] === 1) {
--differ;
} else if (count[s[i].charCodeAt() - 'a'.charCodeAt()] === 0) {
++differ;
}
--count[s[i].charCodeAt() - 'a'.charCodeAt()];
if (count[s[i + pLen].charCodeAt() - 'a'.charCodeAt()] === -1) {
--differ;
} else if (count[s[i + pLen].charCodeAt() - 'a'.charCodeAt()] === 0) {
++differ;
}
++count[s[i + pLen].charCodeAt() - 'a'.charCodeAt()];
if (differ === 0) {
ans.push(i + 1);
}
}
return ans;
};
时间复杂度O(1)