• 【30. 串联所有单词的子串】


    来源:力扣(LeetCode)

    描述:

    给定一个字符串 s 和一些 长度相同 的单词 words 。找出 s 中恰好可以由 words 中所有单词串联形成的子串的起始位置。

    注意子串要与 words 中的单词完全匹配,中间不能有其他字符 ,但不需要考虑 words 中单词串联的顺序。

    示例 1:

    输入:s = "barfoothefoobarman", words = ["foo","bar"]
    输出:[0,9]
    解释:
    从索引 09 开始的子串分别是 "barfoo""foobar" 。
    输出的顺序不重要, [9,0] 也是有效答案。
    
    • 1
    • 2
    • 3
    • 4
    • 5

    示例 2:

    输入:s = "wordgoodgoodgoodbestword", words = ["word","good","best","word"]
    输出:[]
    
    • 1
    • 2

    示例 3:

    输入:s = "barfoofoobarthefoobarman", words = ["bar","foo","the"]
    输出:[6,9,12]
    
    • 1
    • 2

    提示:

    • 1 <= s.length <= 104
    • s 由小写英文字母组成
    • 1 <= words.length <= 5000
    • 1 <= words[i].length <= 30
    • words[i] 由小写英文字母组成

    方法 :滑动窗口

    思路

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

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

    代码:

    class Solution {
    public:
        vector<int> findSubstring(string &s, vector<string> &words) {
            vector<int> res;
            int m = words.size(), n = words[0].size(), ls = s.size();
            for (int i = 0; i < n && i + m * n <= ls; ++i) {
                unordered_map<string, int> differ;
                for (int j = 0; j < m; ++j) {
                    ++differ[s.substr(i + j * n, n)];
                }
                for (string &word: words) {
                    if (--differ[word] == 0) {
                        differ.erase(word);
                    }
                }
                for (int start = i; start < ls - m * n + 1; start += n) {
                    if (start != i) {
                        string word = s.substr(start + (m - 1) * n, n);
                        if (++differ[word] == 0) {
                            differ.erase(word);
                        }
                        word = s.substr(start - n, n);
                        if (--differ[word] == 0) {
                            differ.erase(word);
                        }
                    }
                    if (differ.empty()) {
                        res.emplace_back(start);
                    }
                }
            }
            return res;
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    执行用时:56 ms, 在所有 C++ 提交中击败了72.93%的用户
    内存消耗:33.1 MB, 在所有 C++ 提交中击败了34.82%的用户在这里插入图片描述
    author:LeetCode-Solution

  • 相关阅读:
    服装公司事务/办理流程是如何办理的?
    java-net-php-python-springboot药膳食疗系统计算机毕业设计程序
    双向链表的基本操作
    vue 实现富文本(quill-editor 插件)
    【HiveSQL】join关联on和where的区别及效率对比
    Office Tool Plus下载与神龙版官网下载
    bind搭建内网DNS服务器架构(主从、子域授权、DNS转发器)
    python金融分析小知识(38)——Jupyter Notebook更改文件路径
    Docker 系列之 .Net Core 控制台和 Asp.net Core 服务生成镜像(DockerFile)
    最优化建模、算法与理论(三)—— 优化建模
  • 原文地址:https://blog.csdn.net/Sugar_wolf/article/details/125420110