76. 最小覆盖子串(Minimum Window Substring)

题目描述:

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

示例:

输入: S = "ADOBECODEBANC", T = "ABC"
输出: "BANC"
说明:

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

解题思路:

  这道题用双指针滑动窗口完成。大体思路是记录一个区间,当区间符合要求时,就滑动左边界收缩区间;当区间不符合要求时,就滑动右边界放大区间。每当区间符合要求时,就比较此时区间的值是否是最小值,如果是就更新最小值。

  在这道题中,判断区间是否符合要求稍微复杂一些,需要使用两个unordered_map,一个保存需要满足的字符,一个保存当前区间中的字符。还需要一个整数变量记录符合要求的字符数,从而避免每次都要遍历两个map来判断是否符合条件。过程如下:

  只要右边界没有到s的最大长度,就一直循环。循环中每次把右边界的值加入到区间中,假设加入的字符用ch表示,那么如果ch不在need_map中时,表示该字符没用,就继续循环。如果ch在need_map中,就令window_map中该字符对应的值加1,如果该值等于need_map中字符对应的值,就表示这个字符已经满足了,记录字符个数的变量matched_num加1。接着判断matched_num是否等于need_map中字符的数量,如果等于就表示该区间符合要求,就要收缩左边界。如果左边界的字符ch不在need_map中,同样说明该字符没用,继续收缩左边界。直到左边界字符ch在need_map中时,将window_map中ch对应的值减一,如果该值小于need_map中字符对应的值,就将matched_num减一,此时会跳出收缩左边界循环。

  注意每次区间符合条件时,都要记录此时的区间长度和起始位置,最后返回区间长度最小的子串。

  代码如下:

class Solution {
public:
    string minWindow(string s, string t) {
        int l = 0, r = 0, match_num = 0;
        unordered_map<char, int> needs, windows;
        int len = s.size() + 1, begin_pos;

        for (auto && ch : t)
            ++needs[ch];
        while (r != s.size()) {
            if (needs.count(s[r]) == 0) {
                ++r;
                continue;
            }
            if (++windows[s[r]] == needs[s[r]])
                ++match_num;
            while (match_num == needs.size()) {
                if (len > r - l + 1) {
                    len = r - l + 1;
                    begin_pos = l;
                }
                if (needs.count(s[l]) == 0) {
                    ++l;
                    continue;
                }
                if (--windows[s[l]] < needs[s[l]])
                    --match_num;
                ++l;
            }
            ++r;
        }
        if (len == s.size() + 1)
            return "";
        else
            return s.substr(begin_pos, len);
    }
};
原文地址:https://www.cnblogs.com/yxsrt/p/13236952.html