【LeetCode-滑动窗口】最小覆盖子串

题目描述

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

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

说明:

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

题目链接: https://leetcode-cn.com/problems/minimum-window-substring/

思路

使用滑动窗口来做,使用两个指针 left 和 right 来标记窗口的大小,窗口大小为 right-left+1。left 和 right 初始化为 0,首先将 right 指向的元素加入窗口中并增加 right,直到窗口中包含 T 中所有元素,然后通过增加 left 来缩小窗口大小。算法具体过程如下:

我们使用一个哈希表cnt来记录 T 中元素出现的个数。那么,对于一个字符 c ,如果 c 在 T 中出现了,则 cnt[c]>0;如果 c 没有在 T 中出现,则 cnt[c]==0。使用 left 和 right 来遍历字符串 S,假设当前 right 指向的字符为 c ,将 cnt[c]--,如果 c 在 T 中出现,那么现在 cnt[c]>=0(之所以会大于 0 是因为 T 中可能会包含重复字符),否则 cnt[c]<0。如果减一后 cnt[c]>=0,说明我们在 S 中找到了一个在 T 中出现的字符,则当前找到的字符长度 curLen++。如果 curLen==T.length(),说明我们现在滑动窗口中已经包含了 T 中的所有字符,则可以通过增加 left 来缩小滑动窗口的大小。将 cnt[s[left]]++,如果 s[left] 是 T 中的字符,则加一后的 cnt[s[left]]>0,将 left+1 后,滑动窗口中就不完全包含 T 中的所有字符了(少了一个),这时停止缩小窗口的大小,也就是停止增大 left。如果加一后的 cnt[s[left]]<0,则说明 s[left] 没有在 T 中出现,可以继续缩小窗口。

循环结束的条件是 right>=S.length().

具体代码如下:

class Solution {
public:
    string minWindow(string s, string t) {
        unordered_map<char, int> cnt;
        for(auto c:t) cnt[c]++;  // 统计 t 中字符出现的次数

        int left=0, right=0;
        int minLen=INT_MAX;
        int curLen=0;  // 当前在s中已经找到的字符个数
        string ans="";
        while(right<s.length()){
            cnt[s[right]]--;
            if(cnt[s[right]]>=0) curLen++;

            while(curLen==t.length()){
                int windowSize=right-left+1; // 窗口大小
                if(windowSize<minLen){  // 找到了更短的符合要求的字符串
                    minLen=windowSize;
                    ans = s.substr(left, windowSize);
                }
                cnt[s[left]]++;
                if(cnt[s[left]]>0) curLen--; // 窗口中的字符不全
                left++;
            }
            right++;
        }
        return ans; 
    }
};
  • 时间复杂度:O(n)
    n 为 S 的长度。
  • 空间复杂度:O(n)

参考

https://leetcode-cn.com/problems/minimum-window-substring/solution/yi-bu-bu-xing-cheng-hua-dong-chuang-kou-si-lu-shen/

猜你喜欢

转载自www.cnblogs.com/flix/p/12944630.html
今日推荐