给定一个字符串 s 和一些长度相同的单词 words。找出 s 中恰好可以由 words 中所有单词串联形成的子串的起始位置。
注意子串要与 words 中的单词完全匹配,中间不能有其他字符,但不需要考虑 words 中单词串联的顺序。
示例 1:
输入:
s = “barfoothefoobarman”,
words = [“foo”,”bar”]
输出:[0,9]
解释:
从索引 0 和 9 开始的子串分别是 “barfoo” 和 “foobar” 。
输出的顺序不重要, [9,0] 也是有效答案。
示例 2:
输入:
s = “wordgoodgoodgoodbestword”,
words = [“word”,”good”,”best”,”word”]
输出:[]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/substring-with-concatenation-of-all-words
思路:
- 维护单词长度个滑动窗口,每个单词长度相同,所以每次滑动固定长度。
- 如果当前单词不在words中,说明这个窗口内的内容无法继续匹配了,重置计数器和左指针。
- 在其中时需要考虑如果该单词个数大于words中个数时,需要移动左指针,以及将最左侧单词计数减一。
- 考虑一些边界条件,例如s长度比words中单词长度短,s或words为空。
复杂度分析:
时间复杂度O(n)
空间复杂度O(n)
/**
* @param {string} s
* @param {string[]} words
* @return {number[]}
*/
var findSubstring = function(s, words) {
if(words.length===0 || s.length === 0) return [];
const Counter = (array)=>{
let map = new Map();
for(let i = 0;array && i < array.length; i++){
let tmp = map.get(array[i]);
if(tmp !== undefined){
map.set(array[i],tmp+1);
}else{
map.set(array[i],1);
}
}
return map;
}
let word_num = words.length;
let one_word = words[0].length;
let n = s.length;
if(n < one_word) return [];
let ans = [];
words = Counter(words);
for(let i = 0;i < one_word ;i++){
let left = i;
let right = i;
let cur_map = Counter();
let cnt = 0;
while(right + one_word <= n){
let w = s.slice(right,right+one_word);
right += one_word;
if(words.get(w)){
cnt += 1;
if(cur_map.get(w)){
cur_map.set(w,cur_map.get(w)+1);
}else{
cur_map.set(w,1);
}
while(cur_map.get(w) > words.get(w)){
let left_w = s.slice(left,left+one_word);
left += one_word;
if(cur_map.get(left_w)){
cur_map.set(left_w,cur_map.get(left_w)-1);
}else{
cur_map.set(left_w,0);
}
cnt -=1;
}
if(cnt === word_num) ans.push(left)
}else{
cnt = 0;
left = right;
cur_map.clear();
}
}
}
return ans;
};