LC76 最小覆盖子串

72 阅读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 的子串中,
因此没有符合条件的子字符串,返回空字符串。

提示:

  • m == s.length
  • n == t.length
  • 1 <= m, n <= 10^5
  • s 和 t 由英文字母组成

思路

  1. m,n最大值为105,o(n2)肯定会超时,所以先考虑循环一次能否得到答案
  2. 读题可知不用考虑字符顺序,且t中会存在重复字符,所以字符数量也是需要在考虑的范围中,如此可以考虑用Map记录所有t中的字符和对应数量.
  3. 循环s,当Map中存在时,count++,Mapvalue--,那么当count = t.length()我们就获得了第一个包含t所有字符的子串.
  4. 这个时候吐出前面拿到子串左边的第一个字符,count--,这样让循环继续试试后面会不会有更短的子串

典型滑动窗口即可解决。

代码

public String minWindow(String s, String t) {
    //用来存放t所有char和对应的数量
    Map<Character, Integer> charIndexMap = new HashMap<>()        
    //把所有t放进map中
    for(char c : t.toCharArray()) charIndexMap.put(c, charIndexMap.getOrDefault(c, 0) + 1);
    //当前滑动窗口最左下标
    int left = 0;
    //当前滑动窗口包含t中元素的数量
    int count = 0;
    //最小子串的最左下标
    int minStart = 0;
    //最小子串的长度(记录长度是为了方便后面比较长短)
    int minLength = Integer.MAX_VALUE;
    for (int i = 0; i < s.length(); i++) {
        char c = s.charAt(i);
        if (charIndexMap.containsKey(c)) {
            if (charIndexMap.get(c) > 0) {
                count++;
            }
            //无论当前滑动窗口是否有t的元素,都应记录已出现的元素数量,可能为负数
            charIndexMap.put(c, charIndexMap.get(c) - 1);
        }
        while (count == t.length()) {
            //当滑动窗口包含t的所有字符时判断这个窗口是否更短
            if (i - left + 1 < minLength) {
                minLength = i - left + 1;
                minStart = left;
            }
            //开始吐出最左的第一个t中的元素
            if (charIndexMap.containsKey(s.charAt(left))) {
                charIndexMap.put(s.charAt(left), charIndexMap.get(s.charAt(left)) + 1);
                //这就是上面可能为负数的操作的原因,过多的重复元素不好使
                if(charIndexMap.get(s.charAt(left)) > 0)count--;
            }
            //更新当前滑动窗口边界
            left++;
        }
    }
    if(minLength == Integer.MAX_VALUE) return "";
    return s.substring(minStart, minStart + minLength);
}