leetcode每日一题--76. 最小覆盖子串

  1. 题目描述
  2. 题解
  3. 代码
  4. 推荐与闲话

题目描述

给你一个字符串 S、一个字符串 T,请在字符串 S 里面找出:包含 T 所有字符的最小子串。

实例

输入: S = “ADOBECODEBANC”, T = “ABC”
输出: “BANC”

说明

如果 S 中不存这样的子串,则返回空字符串 “”。
如果 S 中存在这样的子串,我们保证它是唯一的答案。

思路

对于这题目,一开始我只能想到一个暴力求解法(暴力大法好),其实自己想一下就很清楚他的时间复杂度为O(s^3+t) (S是字符串的长度 T是字符串的长度)所以oj会崩的.这个暴力求解就不行.
我看了leetcode中一个大佬的题解,就学到了有一种方法,现在分享给你.
这个大佬是labuladong,这个大佬讲的特别清楚,他有一个微信公众号,可以关注一波(绝对没有收钱纯良心推荐)
这是一个模板,专门解决子串、子数组问题,这是滑动窗口的算法.这个理解很简单,但是各种细节问题很多。比如说如何向窗口中添加新元素,如何缩小窗口,在窗口滑动的哪个阶段更新结果。即便你明白了这些细节,也容易出 bug,找 bug 还不知道怎么找,真的挺让人心烦的。我深受其害.好好学!
首先简单讲一下滑动窗口的一个算法的理解.

  1. 既然叫窗口,肯定有他的道理.在一个字符串S上建立一个窗口,框住你想要框住的字符,如果用数学话讲的话就是区间.这是一个左闭右开的区间[).
  2. 有了窗口,那么怎么框呢?用双指针中的左右指针的技巧.我们开始初始化left=0,right=0,left代表左边的字符,right代表右边的字符.这就可以表示一个区间了.
  3. 我们不断使right++增大他的窗口,知道所有的T字符串的字符全在窗口内.
  4. 那么就要找最优的解!所以我们要使left++减小窗口,达到题目中的最小子串.
  5. 我们需要遍历完,防止有遗漏.

简单说一下,就是先找可行解(最长的解),找到后,缩小,得到最优解(最小的解).
这是大佬的解释对滑动窗口:左右指针轮流前进,窗口大小增增减减,窗口不断向右滑动,这就是「滑动窗口」这个名字的来历。
如果你明白了我上述所说的话,那么你就掌握了滑动窗口的核心思维.
事先说明一下,我比较懒,也菜不会画图,所以以下引用大佬的图片帮助你理解原理.
labuladong
一开始的left和right都为0,接下来我们就要增大right把T中的全框在里面.labuladong
框住之后,我们就要找最优解,所以将left增大减小窗口.
labuladong
如果减小窗口,直到T无法成为窗口内的子串就停下来.
在这里插入图片描述
这样的步骤一直持续下去,直到尾部.

代码

class Solution {
public:
    string minWindow(string s, string t) {
        /* 滑动窗口算法框架 */
    unordered_map<char, int> need, window;
    for (char c : t) need[c]++;//这就是个循环,统计T字符个数
    
    int left = 0, right = 0;
    int valid = 0; 
    int start = 0, len = INT_MAX;
    while (right < s.size()) {
        // c 是将移入窗口的字符
        char c = s[right];
        // 右移窗口
        right++;
        // 进行窗口内数据的一系列更新
        if(need.count(c))
        {
            window[c]++;
            if(window[c]==need[c])
            valid++;

        }

        /*** debug 输出的位置 ***/
        printf("window: [%d, %d)\n", left, right);//检查你的窗口是否正确
        /********************/
        
        // 判断左侧窗口是否要收缩
        while (valid==need.size()) {
            if (right - left < len) {
                start = left;
                len = right - left;
            }
            // d 是将移出窗口的字符
            char d = s[left];
            // 左移窗口
            left++;
            // 进行窗口内数据的一系列更新
            if(need.count(d))
            {
                if(need[d]==window[d])
                valid--;
                window[d]--;
            }
        }
    }
    return len==INT_MAX?"":s.substr(start,len);
}

    
};

我看过一些评论,向你们解释一些小代码的原因:

  1. valid这个变量,我理解是存储T子串中不同字符的个数,在need中,所以对于valid==need.size(),简单解释一下,你可能会想可不可以改成t.size()这是不行的,如果你改了,对于T字符串中重复的字符就不好处理了.
  2. 至于need.count(d)这句话,就是碰到了T中的字符,进行更新和存储.

我觉得你们比我聪明应该不会还有问题,所以解释就到这里,如果你还是有问题,对于一些一句话的代码不理解,请评论.
另外向大家告诉一个小知识,如果你想看源代码,就是主函数的话
点击图中的小图标就可以看到.me
如果你觉得掌握了,可以做以下题目:
找到字符串中所有字母异位词
无重复字符的最长子串
字符串的排列

如果你还不理解,可以看leetcode题解,会有一篇适合你的.

推荐与闲话

我真心推荐labuladong这个大佬的题解,确实很好很清晰,但是这个题解嘛各有各的看法,如果觉得好,符号自己的学习的话关注一波,还有一个就是负雪明烛这个大佬,他的题解也很详细的,而且他一个微信群,可以讨论很多代码方面的问题.这些都是我学习的对象.

对于大一的我,考试周将要到来我也是很慌的,而且我挺爱玩的.这不开了一个自习室在番茄todo,如果需要请评论.
大一千万不要挂科,你挂了就要补考,你想想寝室就你一个补考,到了补考前一天,你在复习他们在开黑这是多么罪恶的事情啊!!!

你们好,我是大一小菜鸡,又菜瘾好大.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值