LC76. 最小覆盖子串

标题

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

注意:

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

示例:

输入:s = "ADOBECODEBANC", t = "ABC"
输出:"BANC"

提示:

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

思路

最朴素的做法:类似于朴素字符串匹配,依次从第一个位置寻找一个符合的字符串,然后在从第二个位置寻找符合的一个字符串。。。从第n位置开始寻找一个符合字符串。寻找出他们其中的最小值,然后输出。(太暴力,超时)

但是我们可以从上述思路中发现,每次寻找的字符串都会建立出一个窗口,此时我们可以尝试一下滑动窗口的思想。

建立一个类似于计算机网络中tcp滑动窗口的概念,当窗口太大了(窗口里的子串包含t中所有元素)我们将窗口左端往右移(减小窗口),当窗口太小了(窗口里的子串不包含t中所有元素)我们将窗口右端右移(扩大窗口)。

注释:我们寻找到是窗口长度的最小值(同时窗口需要包含t中所有元素)

如何知道窗口包含t所有元素:建立一个flag哈希表记录t中所有元素及其出现次数,并与窗口内的元素不断比较(可以用cnt记录窗口内的元素及其出现次数)

代码

class Solution {
public:
    unordered_map<char, int> cnt, flag;
    string minWindow(string s, string t) {
        int len1 = s.size(), len2 = t.size();
        if(len1<len2) return "";
        int l = 0, r = 0; //窗口左端l,右端r
        int minn = 100000; //s中包含t元素的最小子串长度
        string ans = "";  //输出结果
        for(auto &x:t){
            flag[x]++;  //将t中所有元素及出现次数记录下来
        }
        //开始滑动窗口
        while(r<len1){ //窗口右端必须在s串内
            cnt[s[r]]++;  //将右端存入cnt
            while(check()&&l<=r){  //左端滑动
                if(minn>r-l+1){  //比较子串最小长度
                    minn = r-l+1;
                    ans = s.substr(l, minn);
                }
                cnt[s[l]]--;  //将左端元素删除
                l++;  //左端左移
            }
            r++;  //右端右移
        }
        return ans;
    }
		
//检查t中的元素是不是都在窗口内
    bool check(){
        for(auto& [x, y]:flag){
            if(y>cnt[x]){
                return false;
            }
        }
        return true;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值