【专题刷题】滑动窗口(四):

📝前言说明:

  • 本专栏主要记录本人的基础算法学习以及LeetCode刷题记录,按专题划分
  • 每题主要记录:(1)本人解法 + 本人屎山代码;(2)优质解法 + 优质代码;(3)精益求精,更好的解法和独特的思想(如果有的话)
  • 文章中的理解仅为个人理解。如有错误,感谢纠错

🎬个人简介:努力学习ing

📋本专栏:C++刷题专栏

📋其他专栏:C语言入门基础python入门基础C++学习笔记Linux

🎀CSDN主页 愚润泽

视频

  • [30. 串联所有单词的子串](#30. 串联所有单词的子串)
  • [LCR 017. 最小覆盖子串](#LCR 017. 最小覆盖子串)

30. 串联所有单词的子串

个人解

思路(实在写不出了,记录错误想法):

怎么判断当前子串能不能words的单词串联起来(我的想法是利用哈希表,类似第 438. 找异位词)

  • 把word当一个整体,记录words的哈希表
  • 用哈希表记录子串的单词的出现次数
  • 每次 right 和 left 移动的是一个单词的长度,每次改变首尾两个单词在哈希表中的值

用时:30:00

屎山代码(通过了一半用例):

cpp 复制代码
class Solution {
    bool is_connect(unordered_map<string, int> wordsmap, unordered_map<string, int> smap)
    {
        for(auto x: wordsmap)
        {
            if(x.second != smap[x.first])
            {
                return false;
            }
        }
        return true;
    }
public:
    vector<int> findSubstring(string s, vector<string>& words) {
        unordered_map<string, int> wordsmap, smap;
        for(auto x : words) // 获取words的哈希表
        {
            wordsmap[x]++;
        }
        int m = words[0].size(), n1 = s.size(), n2 = words.size();
        vector<int> ans;
        for(int right = m, left = 0; right <= n1; right += m)
        {
            string in(s.begin() + right - m, s.begin() + right);
            smap[in]++; // 进窗口
            if(right - left > n2 * m)
            {
                string out(s.begin() + left, s.begin() + left + m);
                smap[out]--; // 出窗口
                left += m;
            }
            if(right - left == n2 * m && is_connect(wordsmap, smap))
            {
                ans.push_back(left);
            }
        }
        return ans;
    }
};

优质解:

思路:

我的大体思路还是正确的,滑动窗口 + 哈希表

但是问题出在:

我只考虑了紫色线的这种划分,却没有考虑到还可以以绿色线的这种方式划分,以此类推。

因此,滑动窗口要以不同起点开始执行多次,执行的次数是:单个word的长度

这里还有一手很妙的比较smap和wordsmap两个哈希表是否相等的方法:

在遍历s的时候,用一个count记录有效单词的个数,有效单词指:当往smap中加入某一单词后,此时smap中该单词的个数 <= wordsmap里面该单词的个数,即认为有效,count++,直到count的数值等于words里面单词的个数。

那不会加入到很多冗余字符吗?不用担心,因为我们还有长度约束!

代码:

cpp 复制代码
class Solution {
public:
    vector<int> findSubstring(string s, vector<string>& words) {
        unordered_map<string, int> wordsmap;
        for(auto x : words) wordsmap[x]++;  // 获取words的哈希表
        
        vector<int> ans; // 记录答案
        int len = words[0].size(), n = words.size();

        for(int i = 0; i < len; i++) // 进行 len 次滑动窗口
        {
            unordered_map<string, int> smap;
            for(int left = i, right = i, count = 0; right + len <= s.size(); right += len)
            {
                string in = s.substr(right, len); // 进窗口,维护 count
                smap[in]++;
                if(wordsmap.count(in) && smap[in] <= wordsmap[in]) count++; // 判断字符是否有效
                if(right - left + len > len * n) // right - left 反映的是上一次的子串的长度,加len是因为,我们在进窗口的时候,没有更新right的位置。
                {
                    // 出窗口 + 维护 count
                    string out = s.substr(left, len);
                    if(smap[out] <= wordsmap[out]) count--;
                    smap[out]--;
                    left += len;
                }
                if(count == n) ans.push_back(left);
            }
        }
        return ans;
    }
};

时间复杂度:O(n)
空间复杂度:O(1)


LCR 017. 最小覆盖子串

个人解

思路:

这道题没什么好说的了,还是哈希 + 滑动窗口,然后用count来记录有效字符。(把 438 和LCR 017 吃透这题就很简单)

用时:15:00

屎山代码:

cpp 复制代码
class Solution {
public:
    string minWindow(string s, string t) {
        string ans = "";
        int count = 0, min_len = INT_MAX;
        unordered_map<char, int> tmap, smap;
        for(auto x: t) tmap[x]++;

        for(int left = 0, right = 0; right < s.size(); right++)
        {
            char in = s[right]; // 进窗口
            smap[in]++;
            if(tmap.count(in) && smap[in] <= tmap[in]) count++;
            while(count == t.size()) // 判断
            {
                if(ans == "" || right - left + 1 < min_len) // 更新结果
                {
                    min_len = right - left + 1;
                    ans = s.substr(left, min_len);
                }
                char out = s[left];
                if(smap[out] <= tmap[out]) count--;
                smap[out]--;
                left++; // 出窗口
            }
        }
        return ans;
    }
};

时间复杂度:O(n)
空间复杂度:O(1)


🌈我的分享也就到此结束啦🌈

要是我的分享也能对你的学习起到帮助,那简直是太酷啦!

若有不足,还请大家多多指正,我们一起学习交流!

📢公主,王子:点赞👍→收藏⭐→关注🔍

感谢大家的观看和支持!祝大家都能得偿所愿,天天开心!!!

相关推荐
_码力全开_12 分钟前
P1005 [NOIP 2007 提高组] 矩阵取数游戏
java·c语言·c++·python·算法·矩阵·go
墨染点香16 分钟前
LeetCode 刷题【124. 二叉树中的最大路径和、125. 验证回文串】
算法·leetcode·职场和发展
StarPrayers.35 分钟前
损失函数(Loss Function)、反向传播(Backward Propagation)和优化器(Optimizer)学习笔记
人工智能·笔记·深度学习·学习
孤廖42 分钟前
吃透 C++ 栈和队列:stack/queue/priority_queue 用法 + 模拟 + STL 标准实现对比
java·开发语言·数据结构·c++·人工智能·深度学习·算法
小龙报1 小时前
《算法通关指南---C++编程篇(3)》
开发语言·c++·算法·visualstudio·学习方法·visual studio
Mr_WangAndy2 小时前
C++设计模式_行为型模式_状态模式State
c++·设计模式·状态模式
郝学胜-神的一滴2 小时前
Effective STL 第5条:区间成员函数优先于单元素成员函数
开发语言·c++·程序人生·stl·软件工程
2201_758875442 小时前
LeetCode:19. 删除链表的倒数第 N 个结点
算法·leetcode·链表
明月5662 小时前
github开源笔记应用程序项目推荐-Joplin
笔记·开源·joplin·跨平台笔记应用
Wenhao.2 小时前
LeetCode 合并K个升序链表
leetcode·链表·golang