持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第25天,点击查看活动详情
前言
今天的题目为困难,这道题需要掌握题目中的重点,所用到的知识点并不是特别难以理解的算法或者是很难的计算,只需要掌握特定的方法,可能过程会比简单中等题更复杂,但是理清楚逻辑,题目也会变得非常简单。
每日一题
今天的题目是 30. 串联所有单词的子串,难度为困难
-
给定一个字符串 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"]
输出:[]
示例 3:
输入:s = "barfoofoobarthefoobarman", words = ["bar","foo","the"]
输出:[6,9,12]
提示:
- 1 <= s.length <= 104
- s 由小写英文字母组成
- 1 <= words.length <= 5000
- 1 <= words[i].length <= 30
- words[i] 由小写英文字母组成
题解
哈希表+滑动窗口
题目的关键点在于,word 都是相同长度的,也就是说我们知道了其中一个word的长度,其他word的长度就也都能够知道。
并且单词的组成顺序是没有做出限制的,这样我们就能够想到,利用哈希表记录下每个单词出现的次数,然后在滑动窗口的时候,优先去判断当前这个单词是否存在哈希表当中,然后就是记录下这个单词在滑动窗口中出现的次数,两个哈希表中对应的出现次数也需要做出比较,一旦在滑动窗口中出现的次数更多,那么就也可以判断不相等。
并且在每次的成功比较之后,回去记录下当前经历过比较的字符串长度,一旦经过比较的字符串长度等于题目要求的word数组的总长,那么就说明,当前保存下来的子串能够由题目要求的数组拼凑而成。
function findSubstring(s: string, words: string[]): number[] {
let wordsMap = new Map();
for (let word of words) {
wordsMap.set(word, (wordsMap.get(word) || 0)+ 1);
}
let n: number = words[0].length;
let m: number = n * words.length;
let res: number[] = [];
for (let i = 0; i < s.length - m + 1; i++) {
let curWords: string = s.substring(i, i + m);
let map = new Map();
let curLength: number = 0;
for (let j = 0; j < m; j += n) {
let curWord: string = curWords.substring(j, j + n);
map.set(curWord, (map.get(curWord) || 0) + 1);
if (!wordsMap.has(curWord)) {
break;
}
if (map.get(curWord) > wordsMap.get(curWord)) {
break;
}
curLength += n;
}
if (curLength === m) {
res.push(i);
}
}
return res;
};