leetcode76:最小覆盖字串(滑动窗口)

一:题目

在这里插入图片描述

二:思路

思路拿别人的,感觉写的很nice!! 渣渣杰只能膜拜大佬的了

1.滑动窗口的思想:

left 指针和 ring 指针,保证两个指针之间的字符串包含所需要的全部字符。

2在保证 1 的前提下,

向右移动 left,保证 left 的位置为所需要的字符,这样就实现了最短子串。

例如 :s = ABKKKIWI
t = KIWI
left 应该指向 4 而不是 0、1、2 或 3
因为要返回子串,所以我们不直接记录长度,而是使用 min_left 和 min_right 记录最小窗口的位置。

3.关键问题1:怎么确保当前窗口包含所有需要的字符?

可以使用哈希表来记录,key = 需要的 t 中的所有字符,value = 所需字符的个数

例如:s = ABKKKIWI t = KIWI

t_map[‘K’] = 1;

t_map[‘I’] = 2;

t_map[‘w’] = 1;

当向右移动 right 指针时,只要碰到一个 t 中的字符时,对应的哈希表计数 t_map[s[right]] 就减 1

例如:上面的例子,当 right 指针移动到 7 时,哈希表会变为

t_map[‘K’] = -2;

t_map[‘I’] = 0;

t_map[‘w’] = 0;

当所有哈希表中的计数值都不大于 0 时,说明滑动窗口中已经包含了所有需要的字符串。(等于 0 说明窗口中刚好包含所需要的字符 刚好 2 个 I,小于 0 说明窗口中有多余的 t 中的字符 有多余的 K)

但是判断哈希表全部计数值为 0,并不能在 O(1) 时间内实现(需要遍历),因此我们可以使用一个变量 t_len 来记录遍历过的数组,代表还需要找多少个字符。

right 指针向右移动时,当碰到哈希表中存在的字符,且哈希表中的计数值大于0时,说明这种字符还没找够,此时 t_len -=1,right 进行向右移动。如果碰到的字符对应的计数值已经等于0或者小于0,那么 t_len 不能减 1,因为这种字符已经多余了,但哈希表中的计数要接着减。

当 t_len = 0 时,说明窗口内已经包含全部所需要的字符了,接下来只需要移动 left 指针,使得窗口最小即可。

4:关键问题2:怎么把 left 移动到正确位置?

left 正确位置:使得窗口开头就是需要的字符,且不多余。

例如:s = ABKKKIWIWK t = KIWI 当 right 指针移动到 7 时,哈希表会变为

t_map[‘K’] = -2;

t_map[‘I’] = 0;

t_map[‘w’] = 0;

此时的 left 还指向 0, 我们要缩小窗口,AB很容易,不在哈希表中,直接跳过即可,此时哈希表中的 K 计数小于 0 说明存在多余的 K,我们边缩小窗口(即 left ++) 边增加哈希表中的计数,知道计数为0,说明刚好找到了最后一个K(再缩小就没有 K 了),此时窗口为可能的最小窗口,计算该窗口的长度,比较,更新记录即可。

然后 left++ 跳过这个这个 K,使得窗口继续向右滑动寻找下一个 K,寻找下一个子串。

三:上码(一点也不难也就写了5个小时而已)

class Solution {
public:
    /**
    思路:1.滑动窗口类型的题
        2.滑动窗口的三要素
            滑动窗口的起始位置 字符串的起始位置
            滑动窗口的滑动体 在s中包含t的字符串
            滑动窗口的结束位置 当在s中t中的字符全部都遇到了 那么此位置为结束位置   
    */
    string minWindow(string s, string t) {
        map<char,int> m;

        //统计t中各个字符的个数,为了可以确保在找到的字符串中 包含t中的所有元素
        for(int i = 0; i < t.size(); i++) {
          m[t[i]]++;
        }

        int left = 0;
        int minLeft = 0;
        int minRight = s.size();
        int len = t.size();//用于判断找到的字符串中是否包含t的全部元素
     
        for(int right = 0; right < s.size(); right++) {
            //这里表示的是s中的字符在m中,m.find(s[right]) != m.end()  表示存在             
            if(m.find(s[right]) != m.end()){
                if(m[s[right]] > 0){
                    len--;//找到一个元素那么t的长度减一   
                }
                   
                m[s[right]]--;//这里面m[s[right]] 可能会出现负数 因为s中可能会出现多个相同的和t中相关的字符,       
            }
            //说明我们在s找到了一个字符串包含t中的全部字符
            if(len == 0){
                //开始移动左指针,移到包含t中字符的 最小位置为止
                while(m.find(s[left]) == m.end() || m[s[left]] < 0){     
                    if(m.find(s[left]) == m.end()){
                        left++;//说明s中刚开始的字符没在找到的包含t中字符的字符串中
                    }else{
                        m[s[left]]++;
                        left++;//有可能这个字符的在s中有重复的t中的字符,所以我们需要将其加到等于0为止,这是left和right之间包含t中字符的最小个数 
                    }
                }
                
                //更新最小窗口的
                if(right-left < minRight - minLeft){
                    minLeft = left;
                    minRight =right;
                }

                //下一个窗口 也就是包含t中所有字符的另一个字符串
                m[s[left]]++;//恢复s[left]字符的个数
                left++;//淘汰找到包含t的字符串中最左边的字符,准备拥抱新的字符串包含t的所有元素 
                len++;//表示目前的字符串中还差一个字符就可以包含t中的所有字符(为下有一次遍历做准备)
            }
        }


        if(minRight == s.size()){
            return "";
        }else{
            return s.substr(minLeft,minRight-minLeft+1);
        }


    }
};

在这里插入图片描述
这个题是今年秋招 字节一面中半个小时需要AC出来的一到原题,我用了5个小时 哈哈哈哈哈哈哈哈 我也就能无奈的笑笑
自己做了3个小时 没做出来 看大佬思路 又学习人家的码 又用了俩小时 菜鸡杰 菜鸡杰 这回菜到家了 。 本来想放弃 但是如果遇到难的就放弃,那就不用学了,越积越多,虽然花的时间长,但是慢慢来 一切都会好的

认怂 复盘 慢慢来 加油 素未谋面的你 晚安!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天天向上的菜鸡杰!!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值