leetCode 76. 最小覆盖子串 + 滑动窗口 + 图解(详细)

76. 最小覆盖子串 - 力扣(LeetCode)


给你一个字符串 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 的子串中,因此没有符合条件的子字符串,返回空字符串

从左->到下->到右->到下看以下图 

 

(1)思路1:用一个哈希map

class Solution {
public:
    bool check(unordered_map<char,int> mp) {
        for(auto it:mp){
            if(it.second > 0) return false;
	    }
        return true;
    }
    string minWindow(string s, string t) {
        unordered_map<char,int> need;
        int strStart=0,windowLen=INT_MAX;
        int left=0,right=0;
        for(char c:t) {
            need[c]+=1;
        }
        while(right < s.size()) {
            char curChar = s[right];
            if(need.find(curChar)!=need.end()) need[curChar]--;
            while(check(need)) {
                // 更新窗口的长度和起始位置
                int curWindowLen = right-left+1;
                if(curWindowLen < windowLen) {
                    windowLen = curWindowLen;//更新窗口的长度
                    strStart=left;//更新窗口的起始位置
                }
                //继续缩小窗口
                char leftChar = s[left];
                if(need.find(leftChar)!=need.end()) need[leftChar]++;
                left++;
            }
            right++;
        }
        if(windowLen != INT_MAX) return s.substr(strStart,windowLen);
        return "";
    }
};

(2)思路2:实际上还可以把哈希map换成数组,原理其实都是一样的,来看下代码

class Solution {
public:
    string minWindow(string s, string t) {
        vector<int>need(128);
        for(char c:t) {
            need[c]+=1;
        }
        int charCount = t.size();
        int left=0,right=0;
        int strStart=0,windowLen=INT_MAX;
        while(right < s.size()) {
            char curChar = s[right];
            if(need[curChar] > 0) {
                need[curChar]--;
                charCount--;
            }else need[curChar]--;
            while(charCount==0) {
                // 更新窗口的长度和起始位置
                int curWindowLen = right-left+1;
                if(curWindowLen < windowLen) {
                    windowLen = curWindowLen;//更新窗口的长度
                    strStart=left;//更新窗口的起始位置
                }
                //继续缩小窗口
                char leftChar = s[left];
                if(need[leftChar] == 0) {
                    need[leftChar]++;
                    charCount++;
                }else need[leftChar]++;
                left++;
            }
            right++;
        }
        if(windowLen != INT_MAX) return s.substr(strStart,windowLen);
        return "";
    }
};

 我们可以简化一下代码:

if(need[curChar] > 0) {
    need[curChar]--;
    charCount--;
}else need[curChar]--;

可以写成
if(need[curChar]-- > 0) {
    charCount--;
}
if(need[leftChar] == 0) {
    need[leftChar]++;
    charCount++;
}else need[leftChar]++;

可以写成
if(need[leftChar]++ == 0) charCount++;

(2.1)于是,就有如下代码:

class Solution {
public:
    string minWindow(string s, string t) {
        vector<int>need(128);
        for(char c:t) {
            need[c]+=1;
        }
        int charCount = t.size();
        int left=0,right=0;
        int strStart=0,windowLen=INT_MAX;
        while(right < s.size()) {
            char curChar = s[right];
            if(need[curChar]-- > 0) {
                charCount--;
            }
            while(charCount==0) {
                // 更新窗口的长度和起始位置
                int curWindowLen = right-left+1;
                if(curWindowLen < windowLen) {
                    windowLen = curWindowLen;//更新窗口的长度
                    strStart=left;//更新窗口的起始位置
                }
                //继续缩小窗口
                char leftChar = s[left];
                if(need[leftChar]++ == 0) charCount++;
                left++;
            }
            right++;
        }
        if(windowLen != INT_MAX) return s.substr(strStart,windowLen);
        return "";
    }
};

很多题解会写成这样,好处是减少一步加一操作。但是看的时候可能会有点懵

① 思路1的改进:

while(right < s.size()) {
    char curChar = s[right];
    if(need.find(curChar)!=need.end()) need[curChar]--;
    while(check(need)) {
        // 更新窗口的长度和起始位置
        int curWindowLen = right-left+1;
        if(curWindowLen < windowLen) {
            windowLen = curWindowLen;//更新窗口的长度
            strStart=left;//更新窗口的起始位置
        }
        //继续缩小窗口
        ...
        ...
        ...
    }
    right++;
}

========================================================

while(right < s.size()) {
    char curChar = s[right];
    if(need.find(curChar)!=need.end()) need[curChar]--;
    right++;
    while(check(need)) {
        // 更新窗口的长度和起始位置
        if(right-left< windowLen) {
            windowLen = right-left;//更新窗口的长度
            strStart=left;//更新窗口的起始位置
        }
        //继续缩小窗口
        ...
        ...
        ...
    }
}

 ② 思路2的改进

while(right < s.size()) {
    char curChar = s[right];
    if(need[curChar]-- > 0) {
        charCount--;
    }
    while(charCount==0) {
        // 更新窗口的长度和起始位置
        int curWindowLen = right-left+1;
        if(curWindowLen < windowLen) {
            windowLen = curWindowLen;//更新窗口的长度
            strStart=left;//更新窗口的起始位置
        }
        //继续缩小窗口
        ...
        ...
        ...
    }
    right++;
}

========================================================

while(right < s.size()) {
    if(need[s[right++]]-- > 0) {
        charCount--;
    }
    while(charCount==0) {
        // 更新窗口的长度和起始位置
        if(right-left< windowLen) {
            windowLen = right-left;//更新窗口的长度
            strStart=left;//更新窗口的起始位置
        }
        //继续缩小窗口
        ...
        ...
        ...
    }
}

(3) 用两个哈希map

class Solution {
public:
    string minWindow(string s, string t) {
        unordered_map<char,int> need;
        unordered_map<char,int> window;
        for(auto c:t) {
            need[c]+=1;
        }
        int right=0,left=0;
        int valid=0;
        int start=0,minLen=INT_MAX;
        while(right < s.size()) {
            char cur = s[right];
            // 进行窗口数据一系列更新
            if(need.find(cur)!=need.end()) {
                window[cur]++;
                if(window[cur] == need[cur]) valid++;
            }
            while(need.size() == valid) {
                if(right - left + 1 < minLen) {
                    start = left;
                    minLen = right - left + 1;
                }
                // d 是将移除窗口的字符串
                char deleteChar = s[left];
                // 进行窗口内数据当一系列更新
                if(window.find(deleteChar)!=window.end()) {
                    if(window[deleteChar] == need[deleteChar]) valid--;
                    window[deleteChar]--;
                }
                // 左边移动窗口
                left++;
            }
            right++;
        }
        return minLen == INT_MAX ? "" : s.substr(start,minLen);
    }
};

推荐文章:76. 最小覆盖子串 - 力扣(LeetCode)icon-default.png?t=N7T8https://leetcode.cn/problems/minimum-window-substring/solutions/736507/shu-ju-jie-gou-he-suan-fa-hua-dong-chuan-p6ip/


http://www.niftyadmin.cn/n/5127228.html

相关文章

如何收集研究所需的数据?

在进行研究之前&#xff0c;收集数据是至关重要的步骤之一。为了确保收集到的数据准确可靠&#xff0c;我们需要选择合适的收集途径。分享几种常用的方式&#xff0c;供大家参考。 如果需要企业工商数据&#xff0c;求踢踢。 1.调查问卷 调查问卷是一种常见的收集数据的方法&am…

Linux shell脚本

一、shell与内核 软件&#xff1a; 系统软件&#xff1a; os内核&#xff08;Linux和wins不同&#xff09;-操作内核&#xff08;Kernel&#xff09; 不同的os会有不同的壳也就是shell 相当于遥控器 应用软件 硬件 二、bash-命令 1、echo命令 echo "文本"自动换…

uniapp 在 Android Studio 模拟器中运行项目

在开发App时&#xff0c;无论是使用 Flutter 还是 React native&#xff0c;还是使用uni-app 开发跨端App时&#xff0c;总是需要运行调试。一般调试分为两种。 第一&#xff1a;真机调试 第二&#xff1a;模拟器调试 真机调试的好处是可以看到更好的效果&#xff0c;缺点就是…

带你深入理解“栈”(c语言 c++和stl Stack三个版本的模拟实现)

目录 一.栈的概念及结构 二.栈的实现&#xff08;c语言版&#xff09; 2.1静态增长的栈 2.2动态增长的栈 2.3动态栈的模拟实现 1.栈的初始化 2.入栈 3.出栈 4.获取栈顶元素 5.获取栈中有效数据个数 6.检查栈是否为空 7.栈的销毁 三.C 版本模拟实现栈 1.C版本的源代码…

使用 kind 集群实现 all-in-one 在线安装 DCE 5.0 社区版

文章目录 一、什么是DCE 5.0二、部署先决条件三、创建云服务器实例3.1 实例节点配置[最低配置要求]3.2 远程连接云实例3.3 更新系统 四、安装Docker-CE五、使用 Kind 搭建 Kubernetes 集群六、安装依赖包6.1 helm安装6.2 skopeo安装6.3 kubectl安装6.4 yq安装 七、安装 DCE 5.0…

快捷键记录

文章目录 ctrlaltashftwinsWinRCtrlc和CtrlvCtrl -Xshell的复制粘贴ctrlalt&#xff08;鼠标跳出&#xff09;ctrl alt T ctrlalta 这是QQ/TIM的屏幕截图快捷键。截图成功后&#xff0c;会有一栏导航&#xff0c;可以对图片进行勾画、模糊、绘画、标号、撤回、翻译、提取文…

goland无法调试问题解决

goland 无法调试问题解决 golang 版本升级后&#xff0c;goland 无法进行调试了 首先请看自己下载的版本是否有误 1.apple系 M系列芯片的 arm64版本 2.apple系 intel系列芯片的x86_64 3.windows系 intel解决如下&#xff1a; 查看gopath ericsanchezErics-Mac-mini gww-api…

【【萌新的FPGA学习之同步FIFO的代码与tb】】

萌新的FPGA学习之同步FIFO的代码与tb 对于FIFO的介绍在上一节 在这里主要介绍要用如何的判断方法使得FIFO 确定空满 空满信号产生 为产生 FIFO 空满标志&#xff0c;引入 cnt 计数器&#xff0c;cnt 计数器用于指示 FIFO 内部存储数据 个数。 &#xff08;1&#xff09;当只有…