【面试经典150 | 滑动窗口】最小覆盖子串

文章目录

  • 写在前面
  • Tag
  • 题目来源
  • 题目解读
  • 解题思路
    • 方法一:滑动窗口
  • 写在最后

写在前面

本专栏专注于分析与讲解【面试经典150】算法,两到三天更新一篇文章,欢迎催更……

专栏内容以分析题目为主,并附带一些对于本题涉及到的数据结构等内容进行回顾与总结,文章结构大致如下,部分内容会有增删:

  • Tag:介绍本题牵涉到的知识点、数据结构;
  • 题目来源:贴上题目的链接,方便大家查找题目并完成练习;
  • 题目解读:复述题目(确保自己真的理解题目意思),并强调一些题目重点信息;
  • 解题思路:介绍一些解题思路,每种解题思路包括思路讲解、实现代码以及复杂度分析;
  • 知识回忆:针对今天介绍的题目中的重点内容、数据结构进行回顾总结。

Tag

【滑动窗口】【字符串】


题目来源

面试经典150 | 76. 最小覆盖子串

【面试经典150 | 滑动窗口】最小覆盖子串_第1张图片

题目解读

返回字符串 s 中覆盖所有 t 字符串的所有子串,如果 s 中不存在覆盖 t 所有字符的子串,则返回空字符串 ""

注意:

  • 对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。
  • 如果 s 中存在这样的子串,我们保证它是唯一的答案。

解题思路

方法一:滑动窗口

使用滑动窗口来解决本题。首先需要判断在每个滑窗内子串是否覆盖了 t,如果覆盖了,再判断是否可以缩短滑窗的大小。

我们使用一个变量 cnt 来判断滑窗是否覆盖了 t 字符串。 cnt 表示滑窗内覆盖的 t 字符串中的字符个数,比如当前滑窗内的字符串为 "abc"t = acf,则 cnt = 2;初始化 cnt = 0。如果 cnt = s.size(),则滑窗覆盖了 t 字符串。

我们先使用数组 chars 记录 t 中每个字符出现的数量,滑窗内每新增一个 t 字符串中的字符 s[r],则 --chars[s[r]],如果 chars[s[r]] >= 0,则 ++cnt

当滑窗覆盖了 t 之后,更新最小覆盖窗口的左端点位置 min_l 和 最小覆盖窗口的大小 min_size(维护这个变量是为了得到最小覆盖字符串),右移滑窗的左端点(滑窗移除字符),迭代更新最小覆盖字符串窗口。在迭代中,如果滑窗移除的字符是 t 字符串中的字符并且 ++chars[s[l]] > 0,则 --cnt

最后,如果 min_size > s.size(),则表示 s 中没有覆盖 t 的子串,直接返回 "";否则,根据 min_lmin_size 输出最小的覆盖子串。

实现代码

class Solution {
public:
    string minWindow(string s, string t) {
        vector<int> chars(128, 0);
        vector<bool> flag(128, false);

        // 先统计T中的字符情况
        for(int i = 0; i < t.size(); ++i) {
            flag[t[i]] = true;
            ++chars[t[i]];
        }

        //移动滑动窗口,不断更改数据
        int cnt = 0;                    // 匹配的数量
        int l = 0;                      // 左端点
        int min_l = 0;                  // 最小覆盖窗口的左端点位置
        int min_size = s.size() + 1;    // 最小覆盖窗口的大小
        for(int r = 0; r < s.size(); ++r) {
            if(flag[s[r]]) {
                if(--chars[s[r]] >= 0) {
                    ++cnt;
                }

                while(cnt == t.size()) {
                    if(r - l + 1 < min_size) {
                        min_l = l;
                        min_size = r - l + 1;
                    }
                    if(flag[s[l]] && ++chars[s[l]] > 0) {
                        --cnt;
                    }
                    ++l;
                }
            }
        }
        return min_size > s.size() ? "" : s.substr(min_l, min_size);
    }
};

复杂度分析

时间复杂度:最坏情况下左右指针对 s 的每个元素各遍历一遍,哈希表中对 s 中的每个元素各插入、删除一次,对 t 中的元素各插入一次。每次检查是否可行会遍历整个 t 的哈希表,哈希表的大小与字符集的大小有关,设字符集大小为 ∑ \sum ,则渐进时间复杂度为 O ( ∑ ⋅ ∣ s ∣ + ∣ t ∣ ) O(\sum⋅∣s∣+∣t∣) O(s+t)

空间复杂度: O ( ∑ ) O(\sum) O() ∑ = 128 \sum = 128 =128,为记录出现的字符种类。


写在最后

如果文章内容有任何错误或者您对文章有任何疑问,欢迎私信博主或者在评论区指出 。

如果大家有更优的时间、空间复杂度方法,欢迎评论区交流。

最后,感谢您的阅读,如果感到有所收获的话可以给博主点一个 哦。

你可能感兴趣的:(面试经典150题,滑动窗口,字符串,C++,算法)