leetcode:76. 最小覆盖子串

题目来源

题目描述

在这里插入图片描述
在这里插入图片描述

class Solution {
public:
    string minWindow(string s, string t) {

    }
};

题目解析

滑动窗口 + 欠账表

思想:

  • 在滑动窗口类型的问题中都会有两个指针,一个用于[延伸]现有窗口的j指针,一个用于[收缩]窗口的i指针
  • 在任何时刻,都只有一个指针运行,而另一个保持静止
  • 我们在s上滑动窗口,通过移动r指针不断扩张窗口,当窗口包含t全部所需的字符后,如果能够收缩,我们就收缩窗口直到得到最小窗口(j - i + 1)

请添加图片描述
步骤:

  • 不断增加j使滑动窗口增大,直到窗口包含了T的所有元素
  • 不断增加i使滑动窗口缩小,因为是要求最小字串,所以将不必要的元素排除在外,使长度减小,直到碰到一个必须包含的元素,这个时候不能再扔了,再扔就不满足条件了,记录此时滑动窗口的长度,并保存最小值
  • 让i再增加一个位置,这个时候滑动窗口肯定不满足条件了,那么继续从步骤一开始执行,寻找新的满足条件的滑动窗口,如此反复,直到j超出了字符串S范围。

问题是:如果判断滑动窗口中包含了T的元素

  • 通过维护一个need:
    • 步骤一:我们用一个字典need来表示当前滑动窗口中需要的各元素的数量
    • 步骤二:一开始滑动窗口为空,用T中各元素来初始化这个need
    • 步骤三:当滑动窗口扩展或者收缩的时候,去维护这个need字典,例如当滑动窗口包含某个元素,我们就让need中这个元素的数量减1,代表所需元素减少了1个;当滑动窗口移除某个元素,就让need中这个元素的数量加1。
  • 也就是说:
    • need始终记录着当前滑动窗口下,我们还需要的元素数量。
    • 我们在改变i、j的时候,需要同步维护need
  • 注意:
    • need可以为负数,如果某个元素存储的是负数代表这个元素是多余的。比如当need等于{‘A’:-2,‘C’:1}时,表示当前滑动窗口中,我们有2个A是多余的,同时还需要1个C。
    • 这么做的目的就是为了步骤二中,排除不必要的元素,数量为负的就是不必要的元素,而数量为0表示刚刚好。
  • 综上:当need中所有元素的数量都小于等于0时,表示当前滑动窗口不再需要任何元素

优化:

  • 如果每次判断滑动窗口是否包含了T的所有元素,都去遍历need看是否所有元素数量都小于等于0,这个会耗费O(k)的时间复杂度
  • 其实这个是可以避免的,我们可以维护一个额外的变量needCnt来记录所需元素的总数量,当我们碰到一个所需元素c,不仅need[c]的数量减少1,同时needCnt也要减少1,这样我们通过needCnt就可以知道是否满足条件,而无需遍历字典了。
  • 什么叫做所需元素?只有need[c]>0大于0时,代表c就是所需元素
class Solution {
public:
    string minWindow(string s, string t) {
        std::vector<int> need(128, 0);
        for(char c : t){
            need[c]++;
        }
        int count = t.size();
        int i = 0, j = 0, start = 0, size = INT32_MAX;
        while (j < s.size()){
            char c = s[j];
            //先把右边的字符加入窗口
            --need[c];  
            if(need[c] >= 0){
                --count;  //是不是有效还钱
            }

            // 每加入一个字符都判断一下窗口内是不是已经包含了全部字符了
            if(count == 0){  
                //如果是的,那么尝试缩小窗口(增加i,排除多余元素)
                while (i < j && need[s[i]] < 0){// l == r时不形成窗口&&多余的
                    need[s[i++]]++;//将多还的弹出窗口
                }
                //此时窗口符合要求, 更新答案
                if(j - i + 1 < size){
                    size = j - i + 1;
                    start = i;
                }
                // i增加一个位置,寻找新的满足条件滑动窗口
                need[s[i]]++;
                count += 1 ;   //由于 移动前i这个位置 一定是所需的字母,因此count才需要+1
                i++;
            }
            j++;
        }

        return size == INT32_MAX? "" : s.substr(start, size);
    }
};

类似题目

题目思路
leetcode:76. 最小子串:字符串s2覆盖s1的所有子串的最小子串 Minimum Window Substring滑动窗口 + 欠钱表
leetcode:567. 是否包含:字符串s2是否包含s1的排列 Permutation in String滑动窗口 + 欠账表
leetcode:209. 最小子数组长度:无序正数数组其和>=k的长度最小的子数组的长度 Minimum Size Subarray Sum滑动窗口;前缀和 + 二分
leetcode:560. 所有子数组个数:无序整数数组其和 == K 的子数组的个数 Subarray Sum Equals K 主要思路:计算完包括了当前数前缀和之后,我们去查一查在当前数之前,有多少个前缀和等于preSum - k呢?
leetcode:718. 最长子数组长度:两个数组公共的,最长的子数组长度 Maximum Length of Repeated Subarray
leetcode:632. 最小区间:覆盖K个列表元素的最小区间 Smallest Range Covering Elements from K Lists
leetcode:239. 滑动窗口最大值 Sliding Window Maximum💖💖💖💖💖
leetcode:30. 串联所有单词的子串 Substring with Concatenation of All Wordshard级别,暂时不做
leetcode:727. 最小窗口序列Minimum Window Subsequence
leetcode:159.最多有两个不同字符的最长子串滑动窗口
leetcode:904. 水果成篮滑动窗口
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值