当前位置: 首页 > news >正文

LeetCode-76.最小覆盖子串

1、题目描述:

给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 "" 。

注意:

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

示例 1:

输入:s = "ADOBECODEBANC", t = "ABC"
输出:"BANC"
解释:最小覆盖子串 "BANC" 包含来自字符串 t 的 'A'、'B' 和 'C'。

示例 2:

输入:s = "a", t = "a"
输出:"a"
解释:整个字符串 s 是最小覆盖子串。

示例 3:

输入: s = "a", t = "aa"
输出: ""
解释: t 中两个字符 'a' 均应包含在 s 的子串中,
因此没有符合条件的子字符串,返回空字符串。

提示:

  • m == s.length
  • n == t.length
  • 1 <= m, n <= 105
  • s 和 t 由英文字母组成

2、代码:

class Solution {
public:
    string minWindow(string s, string t) {
        // 如果字符串 s 或 t 为空,或者 s 的长度小于 t 的长度,直接返回空字符串
        if (s.empty() || t.empty() || s.size() < t.size()) {
            return "";
        }

        // need 哈希表:记录字符串 t 中每个字符的需求个数
        unordered_map<char, int> need;
        // window 哈希表:记录当前窗口中每个字符的数量
        unordered_map<char, int> window;

        // 初始化 need 哈希表,统计 t 中每个字符的出现次数
        for (char c : t) {
            ++need[c];
        }

        // 定义滑动窗口的左右边界
        int left = 0, right = 0;
        // 记录最小覆盖子串的起始位置和长度
        int start = 0, len = INT_MAX;
        // valid 变量:表示窗口中满足 need 条件的字符个数
        int valid = 0;

        // 开始滑动窗口
        while (right < s.size()) {
            // c 是将要移入窗口的字符
            // 右边界向右扩展
            char c = s[right++];

            // 更新窗口内的数据
            if (need.count(c)) {  // 如果字符 c 在 need 中(即它是 t 中的字符)
                ++window[c];  // 将 c 加入窗口,并更新其计数
                if (window[c] == need[c]) {  // 如果窗口中 c 的数量达到了 need 中的需求
                    ++valid;  // 满足条件的字符个数加一
                }
            }

            // 判断左侧窗口是否需要收缩
            while (valid == need.size()) {  // 当窗口中的字符已经满足了 t 中所有字符的需求时
                // 更新最小覆盖子串
                if (right - left < len) {  // 如果当前窗口比之前记录的最小窗口更小
                    start = left;  // 更新最小窗口的起始位置
                    len = right - left;  // 更新最小窗口的长度
                }

                // d 是将要移出窗口的字符
                // 左边界向右收缩
                char d = s[left++];
                

                // 更新窗口内的数据
                if (need.count(d)) {  // 如果字符 d 在 need 中
                    if (window[d] == need[d]) {  
                    // 如果窗口中 d 的数量刚好等于 need 中的需求
                        --valid;  // 满足条件的字符个数减一
                    }
                    --window[d];  // 将 d 移出窗口,并更新其计数
                }
            }
        }

        // 返回结果
        // 如果 len 仍然是 INT_MAX,说明没有找到符合条件的子串,返回空字符串
        // 否则,返回从 start 开始长度为 len 的子串
        return len == INT_MAX ? "" : s.substr(start, len);
    }
};

3、解题思路:

1. 这是一个滑动窗口问题,需要 left 和 right 指针的滑动去遍历 s 字符串,首先创建2个无序的map(相当于哈希表,访问效率更高),一个need用来存储需要包含的字符种类及其数量(也就是 t 字符串),一个window用来存储 left 和 right 指针截取的 s 字符串中,所包含的字符种类及其数量。

2. 开始循环,循环条件是 right < s.size(),每次循环之前,都会进行 char c = s[right++] 操作,也就是提取 right 当前指向的 s 字符串的字符,并且 right++。

3. 开始扩充window,当 need 里面有 c 时,++window[c] ,如果 need[c] == window[c] 时,代表window 已经包含 need 里面的所有 c 字符了,那么 ++valid,也就是说,某个字符,已经完全满足。

4. 扩从不一定结束,但可以尝试开始收缩window,当valid == need.size()时,开始循环,收缩窗口,当right - left < len 时(哈希表是从 0 开始的,不需要 right - left + 1),更新start 、len的值,然后 char d = s [left++],当 need.count(d)  时,--window[d],当 window[d] == need[d]时,也就是不需要再收缩了,--valid,退出循环

相关文章:

  • 【Pytorch 库】自定义数据集相关的类
  • ffmpeg configure 研究2:分析屏幕输出及文件输出的具体过程
  • STM32 CAN过滤器配置和应用方法介绍
  • Qt QDateTimeEdit总结
  • Hermite 插值
  • 一周学会Flask3 Python Web开发-Flask3 Hello World编写
  • HBase Shell
  • java练习(27)
  • TCP
  • 【MySQL】第一弹---MySQL 在 Centos 7环境安装
  • 【线段树 二分查找】P3939 数颜色|普及+
  • Windows 下打包labelimg 详细过程总结
  • css之display:grid布局改块级元素布局
  • Java工程师技术栈
  • Go语言实现十大排序算法超细节图片讲解
  • Docker-介绍以及安装docker
  • Docker__持续更新......
  • Java延时定时刷新Redis缓存
  • 什么是掉期(Swap)?——金融衍生品的关键工具(中英双语)
  • Spring Boot Actuator 监控✨
  • 老旧小区加装电梯后续维护谁负责?上海:各区属房管集团托底保障
  • “十五五”时期长三角需创新机制,形成高水平一体化合作路径
  • 专利申请全球领先!去年我国卫星导航与位置服务产值超5700亿元
  • 全国游泳冠军赛:孙杨、潘展乐同进400自决赛,今晚将正面对决
  • 南宁一学校发生伤害案件,警方通报:嫌疑人死亡,2人受伤
  • 由我国牵头制定,适老化数字经济国际标准发布