力扣每日一题0623-30. 串联所有单词的子串

62 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第23天,点击查看活动详情

给定一个字符串 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]

滑动窗口

words\textit{words} 的长度为 mmwords\textit{words} 中每个单词的长度为 nnss 的长度为 ls\textit{ls}。首先需要将 ss 划分为单词组,每个单词的大小均为 nn (首尾除外)。这样的划分方法有 nn 种,即先删去前 iii=0n1i = 0 \sim n-1)个字母后,将剩下的字母进行划分,如果末尾有不到 nn 个字母也删去。对这 nn 种划分得到的单词数组分别使用滑动窗口对 words\textit{words} 进行类似于「字母异位词」的搜寻。

划分成单词组后,一个窗口包含 ss 中前 mm 个单词,用一个哈希表 differ\textit{differ} 表示窗口中单词频次和 words\textit{words} 中单词频次之差。初始化 differ\textit{differ} 时,出现在窗口中的单词,每出现一次,相应的值增加 11,出现在 words\textit{words} 中的单词,每出现一次,相应的值减少 11。然后将窗口右移,右侧会加入一个单词,左侧会移出一个单词,并对 differ\textit{differ} 做相应的更新。窗口移动时,若出现 differ\textit{differ} 中值不为 00 的键的数量为 00,则表示这个窗口中的单词频次和 words\textit{words} 中单词频次相同,窗口的左端点是一个待求的起始位置。划分的方法有 nn 种,做 nn 次滑动窗口后,即可找到所有的起始位置。

var findSubstring = function(s, words) {
    const res = [];
    const m = words.length, n = words[0].length, ls = s.length;
    for (let i = 0; i < n; i++) {
        if (i + m * n > ls) {
            break;
        }
        const differ = new Map();
        for (let j = 0; j < m; j++) {
            const word = s.substring(i + j * n, i + (j + 1) * n);
            differ.set(word, (differ.get(word) || 0) + 1);
        }
        for (const word of words) {
            differ.set(word, (differ.get(word) || 0) - 1);
            if (differ.get(word) === 0) {
                differ.delete(word);
            }
        }
        for (let start = i; start < ls - m * n + 1; start += n) {
            if (start !== i) {
                let word = s.substring(start + (m - 1) * n, start + m * n);
                differ.set(word, (differ.get(word) || 0) + 1);
                if (differ.get(word) === 0) {
                    differ.delete(word);
                }
                word = s.substring(start - n, start);
                differ.set(word, (differ.get(word) || 0) - 1);
                if (differ.get(word) === 0) {
                    differ.delete(word);
                }
            }
            if (differ.size === 0) {
                res.push(start);
            }
        }
    }
    return res;
};