32. 最小子串覆盖

描述

给定两个字符串 source 和 target. 求 source 中最短的包含 target 中每一个字符的子串.

  1. 如果没有答案, 返回 "".
  2. 保证答案是唯一的.
  3. target 可能包含重复的字符, 而你的答案需要包含至少相同数量的该字符.

样例

样例 1:

输入: source = "abc", target = "ac"
输出: "abc"

样例 2:

输入: source = "adobecodebanc", target = "abc"
输出: "banc"
解释: "banc" 是 source 的包含 target 的每一个字符的最短的子串.

样例 3:

输入: source = "abc", target = "aa"
输出: ""
解释: 没有子串包含两个 'a'.

挑战

O(n) 时间复杂度

题解:

class Solution {
public:
    /**
     * @param source : A string
     * @param target: A string
     * @return: A string denote the minimum window, return "" if there is no such a string
     */
    string minWindow(string &source , string &target) {
        // 初始化counter_s和counter_t
        unordered_map<char, int> counter_t, counter_s;
        for (char c : target) {
            counter_t[c]++;
        }

        int left = 0, valid = 0;
        // 记录最小覆盖子串的起始索引及长度
        int start = -1, minlen = INT_MAX;
        for (int right = 0; right < source.size(); right ++){
            // 移动右边界, ch 是将移入窗口的字符
            char ch = source[right];
            if (counter_t.count(ch)) {
                counter_s[ch]++;
                if (counter_s[ch] == counter_t[ch])
                    valid++;
            }

            // 判断左侧窗口是否要收缩
            while (valid == counter_t.size()) {
                // 更新最小覆盖子串
                if (right - left < minlen) {
                    start = left;
                    minlen = right - left;
                }
            // left_ch 是将移出窗口的字符
            char left_ch = source[left];
            // 左移窗口
            left ++;
            // 进行窗口内数据的一系列更新
            if (counter_t.count(left_ch)) {
                if (counter_s[left_ch] == counter_t[left_ch])
                    valid--;
                counter_s[left_ch] --;
            }                    
        }
    }
    // 返回最小覆盖子串
    return start == -1 ? "" : source.substr(start, minlen + 1);
    }
};
posted @ 2020-12-25 22:45  你的深渊  阅读(156)  评论(0编辑  收藏  举报