「LeetCode」30-串联所有单词的子串

116 阅读1分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 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] 由小写英文字母组成

二、思路分析:

首先这道题目是一道困难的题目,它给出一个长字符串,要求我们能够从这个字符串中找到完全符合单词数组的单词,且那一串字符串不论如何排序必须是完全匹配,即中间不能有其他字符,这代表着如果截取的字符串里面有其他字符则不符合,返回每个单词在字符串中的起始位置。所以这道题目明显告诉我们需要利用到滑动窗口进行题目的求解:

  • 首先因为单词数组的每个单词长度是完全一致的,所以我们滑动窗口的滑动步数是根据单词长度来决定的。
  • 对于每个单词我们都需要先添加一个Map映射来存储出现的次数。
  • 在循环中我们每次需要记录一个单词长度的字符串,如果发现这个截取的字符串出现在单词Map映射中,还需要判断出现的次数是否匹配的上,如果超过了则直接进行下一次循环,如果截取的字符串不在单词映射中,那么也需要进行下一次循环。
  • 如果在二重循环中,我们每个单词长度的字符串全部符合单词Map映射,那么我们就可以将i推入结果数组中,知道循环结束输出结果。

三、代码:

  function findSubstring(s: string, words: string[]): number[] {
    //利用到了滑动窗口,每次移动的步数是word单词的长度然后进行一一比对
    const wordMap = new Map<string,number>(), res = new Array<number>();
    const n = words.length, step = words[0].length;
    for(const word of words){
        wordMap.set(word, (wordMap.get(word) || 0) + 1);
    }
    entry:
    for(let i=0 ; i<= s.length - step * n ; i++){
        const curWord = new Map<string,number>();
        for(let j=0 ; j<n ; j++){
            const subStr = s.substr(i + j * step, step);
            if(!wordMap.has(subStr)){
                continue entry
            }
            if(curWord.has(subStr)){
                curWord.set(subStr, curWord.get(subStr) + 1);
                if(curWord.get(subStr) > wordMap.get(subStr)){
                    continue entry
                }
            }else{
                curWord.set(subStr,1);
            }
        }
        res.push(i);
    }
    return res
};

四、总结:

这道题目用到了滑动窗口的思路,而且还添加了两个Map映射,一个是构建给出的单词组的映射,一个是作为在滑动窗口里面截取到的字符串的映射,利用构建的Map进行一一比对,如果全部符合那么将起始位置的i放入结果数组中,如果不符合滑动窗口向前移动直到不能移动为止。