难度:Hard

标签:哈希表;滑动窗口;字符串

链接: https://leetcode.cn/problems/minimum-window-substring/description/

比较容易想到要用滑动窗口来解决,使用两个哈希表来记录信息:cnt_t用于记录字符串t中每个字符出现过的次数,cnt_s用于滑动窗口中的字符的出现次数。

滑动窗口的区间为[left, right],并记录最小的左右区间(这里我使用一个res数组)。移动right有区间,直到移动到s字符串结束部分。将s[right]加入cnt_s中,如果cnt_s包含cnt_t,则:

  1. 若当前区间长度小于记录的最小区间长度,更新这个最小区间长度;
  2. 将cnt_s中s[left]出现的次数-1;
  3. left右移+1;
  4. 重复上面3步,直到cnt_s不包含cnt_t;

最后,若res[0] < 0(最小左区间),说明s 中不存在涵盖 t 所有字符的子串,则返回空字符串 "" ;反之,返回最小左右区间中的字符串。

这里的“cnt_s包含cnt_t”是什么意思呢?cnt_t中有的字符cnt_s中都要有,并且cnt_t中字符的次数要小于等于cnt_s中字符出现的次数。

class Solution {
public:
    string minWindow(string s, string t) {
        unordered_map<char, int> cnt_s, cnt_t;
        for (char c : t) {
            cnt_t[c]++;
        }
        int left = 0, right = 0, n = s.length();
        int res[2]{-1, n};
        while (right < n) {
            cnt_s[s[right]]++;
            while (cover(cnt_s, cnt_t)) {
                if (right - left < res[1] - res[0]) {
                    res[0] = left;
                    res[1] = right;
                }
                cnt_s[s[left++]]--;
            }
            right++;
        }
        return res[0] < 0 ? "" : s.substr(res[0], res[1] - res[0] + 1);
    }

    bool cover(unordered_map<char, int>& cnt_s, unordered_map<char, int>& cnt_t) {
        for (auto& [k, v] : cnt_t) {
            if (cnt_s.find(k) == cnt_t.end() || cnt_s[k] < v) {
                return false;
            }
        }
        return true;
    }
};