LeetCode76. 最小覆盖子串

滑动窗口(双指针)

要在字符串S里找出包含字符串T的所有字符的最小子串(注意只需要包含字符串T的所有字符),不需要子串就是T。

所以我们可以扫描一遍字符串S,找出一个满足条件包含字符串T的所有字符串子串,然后根据长度是否比之前记录的字符串长度小,更新最小子串。

扫描字符串S需要用到滑动窗口(双指针),指针j表示子串的起始位置,子串i表示字符串的结束位置。
由于需要计算字符的出现次数,所以考虑用哈希表。

开两个<char, int>型哈希表hashT和hashWindow,hashT表示字符串中各个字符的出现次数,
hashWindow表示当前子串中各个字符的出现次数。

有了哈希表,还需要判断子串中是否包含了字符串T中的所有字符呢,这里我们引入一个变量cnt表示有效数字,
有效数字的计算规则是:遍历子串,如果字符是字符串T中的字符且出现次数没有超过字符串T中该字符出现的次数,则cnt加一。
因为子串中可能包含许多冗余字符。比如某字符s[i]虽然是T中的字符,
但是在子串中的出现次数已经超过了T中该字符出现的次数(hashWindow[s[i]] > hashT[s[i]]),这时cnt是不会加一的。
只有字符是T中字符,且在子串中出现次数小于T中出现次数时,有效数字cnt才会加一。

有了cnt之后,只要cnt和字符串T的大小相等,就可以认为当前子串包含了字符串T中的所有字符,这时再更新一下满足题意的最小子串即可。

代码如下:

class Solution {
public:
    string minWindow(string s, string t) {
        unordered_map<char, int> hashT, hashWindow;            //两个哈希表分别表示字符串t和当前子串中各个字符出现的次数
        for(auto c : t) {                              //计算t中各字符出现次数
            ++hashT[c];
        }
        string res;
        int cnt = 0;
        for(int i = 0, j = 0; i < s.size(); ++i) {      //字符串s的j ~ i部分为滑动窗口,表示当前子串
            ++hashWindow[s[i]];                         
            if(hashWindow[s[i]] <= hashT[s[i]]) {       //新增加的字符s[i]是t中字符且出现次数小于等于t中该字符出现次数,有效数字加一
                ++cnt;
            }
            while(hashWindow[s[j]] > hashT[s[j]]) {      //由于为了新增加一个有效字符s[i],中间可能加入了很多冗余字符,因此需要判断子串的左边部分是否可以删减
                --hashWindow[s[j]];                  //删减的条件就是,子串左边的字符出现次数已经超过了字符串t中该字符出现的次数,这时左指针可以右移,问题不大
                ++j;
            }
            if(cnt == t.size()) {                        //有效数字等于字符串t的大小,表示当前子串包含t中的所有字符
                if(res.empty() || res.size() > i - j + 1) {      //如果res为空(表示现在这个子串是第一个满足条件的子串)或者当前子串长度小于之前记录的子串
                    res = s.substr(j, i - j + 1);            //更新最小子串
                }
            }
        }
        return res;
    }
};
原文地址:https://www.cnblogs.com/linrj/p/13237186.html