题目描述

124 阅读2分钟

题目描述

给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 "" 。

注意:

  • 对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。
  • 如果 s 中存在这样的子串,我们保证它是唯一的答案。

示例 1:

**输入:**s = "ADOBECODEBANC", t = "ABC"

输出:"BANC"

**解释:**最小覆盖子串 "BANC" 包含来自字符串 t 的 'A'、'B' 和 'C'。

示例 2:

**输入:**s = "a", t = "a"

输出:"a"

**解释:**整个字符串 s 是最小覆盖子串。

示例 3:

**输入:**s = "a", t = "aa"

输出: ""

**解释:**t 中两个字符 'a' 均应包含在 s 的子串中, 因此没有符合条件的子字符串,返回空字符串。

提示:

  • s t 由英文字母组成
  • m == s.length
  • n == t.length
  • 1 <= m, n <= 105

解题思路

从题目的描述的可以看出,要求的子串在原串中带有起始位置和终止位置的连续字符串,那么,这时可以考虑使用滑动窗口来求解。在本题中实现滑动窗口,需要确定如下三点:

  • 窗口内是什么?
  • 如何移动窗口的起始位置?
  • 如何移动窗口的终止位置?

窗口就是 s 中涵盖 t 所有字符的子串。

窗口的起始位置如何移动:

起始位置移动分析

我们可以发现,当找到这样的子串时,它可能并不是最小的,因为对于 t 中重复字符,我们寻找的子字符串中该字符数量可能多于t 中该字符数量,例如下图这种情况:

这时我们就需要从找到的子串中削减重复字符的数量,直到某个字符的数量少于t中相应字符的数量,那么此时得到的子串就是最小的。

基于此,我们可以得出:当在s中找到符合条件的子串时,起始位置不断向前移动,每移动一次,所指向字符的数量减少1,直到某个字符的数量少于t中相应字符的数量时停止,得到当前最小子串。

窗口的终止位置如何移动:终止就是遍历字符串s的指针。

代码实现

class Solution {
    public String minWindow(String s, String t) {
        int slen = s.length(), tlen = t.length();
        // 存储s和t中不同字符的数量
        int[] sc = new int[128];
        int[] tc = new int[128];
        // count用来判断初始子串是否已找到,tempLen表示上一个最小子串的长度
        int count = tlen, tempLen = 1000000;
        // 窗口起始与终止指针
        int left = 0, right = 0;
        // 最终结果
        String res = "";
        // 统计字符串t中的不同字符数量
        for (int i = 0; i < tlen; ++i) {
            char c = t.charAt(i);
            tc[c]++;
        }
        for (; right < slen; ++right) {
            char c = s.charAt(right);
            /**
                当s中对应t中相应字符数量相等时,减去该数量。
                当count小于或等于0时,满足条件的初始子串已找到,
                其中小于0的情况代表t中有重复字符
            */
            if (tc[c] == ++sc[c]) count -= tc[c];
            if (count <= 0) {
                while(left <= right) {
                    c = s.charAt(left++);
                    if (tc[c] > --sc[c]){
                        String ts = s.substring(left-1, right + 1);
                        if(ts.length()<tempLen){
                            tempLen=ts.length();
                            res = ts;
                        }
                        break;
                    }
                }
                // t中每个字符至少出现一次。
                count = 1;                
            }
        }
        return res;
    }
}