给定一个字符串 s 和一个字符串数组 words。 words 中所有字符串 长度相同。
s 中的 串联子串 是指一个包含 words 中所有字符串以任意顺序排列连接起来的子串。
例如,如果 words = [“ab”,“cd”,“ef”], 那么 “abcdef”, “abefcd”,“cdabef”,
“cdefab”,“efabcd”, 和 “efcdab” 都是串联子串。 “acdbef” 不是串联子串,因为他不是任何 words
排列的连接。 返回所有串联字串在 s 中的开始索引。你可以以 任意顺序 返回答案。
(力扣第30题)
输入:s = “barfoothefoobarman”, words = [“foo”,“bar”]
输出:[0,9]
解释:因为 words.length == 2 同时 words[i].length == 3,连接的子字符串的长度必须为 6。
子串 “barfoo” 开始位置是 0。它是 words 中以 [“bar”,“foo”] 顺序排列的连接。
子串 “foobar” 开始位置是 9。它是 words 中以 [“foo”,“bar”] 顺序排列的连接。
输出顺序无关紧要。返回 [9,0] 也是可以的。
思路:滑动窗口
注意:substring是左闭右开
/**
* @param {string} s
* @param {string[]} words
* @return {number[]}
*/
var findSubstring = function(s, words) {
const res = [];
const m = words.length;//数组中单词的个数
const n = words[0].length;//单词的长度
const ls = s.length; //串的长度
for(let i =0;i<n;i++){//有n种分割方法
if(i+m*n>ls){
break;
}
const map = new Map();
for(let j=0;j<m;j++){
let word = s.substring(i+j*n,i+(j+1)*n)
map.set(word,(map.get(word)||0)+1)
}
for(let word of words){
map.set(word,(map.get(word)||0)-1);
if(map.get(word)===0){
map.delete(word)
}
}
//初始完毕窗口内容,开始滑动
for(let start=i;start<ls-m*n+1;start+=n){
//start=i时,即为初始化窗口,这时不用滑动,只需要判断,然后继续滑动
if(start!==i){
let word = s.substring(start+(m-1)*n,start+m*n)
map.set(word,(map.get(word)||0)+1);
if(map.get(word)===0){
map.delete(word)
}
word = s.substring(start-n,start);
map.set(word,(map.get(word)||0)-1);
if(map.get(word)===0){
map.delete(word)
}
}
if(map.size===0){//当map长度是0的时候,说明单词的出现次数一样,被抵消了,记录start
res.push(start)
}
}
}
return res;
};