76.最小覆盖子串

179 阅读2分钟

76. 最小覆盖子串 - 力扣(LeetCode)

关键算法知识点: 滑动窗口(双指针) + 哈希表

75e1c3a330b3707b961c273e3689f70e.jpg 做题思路: 扩展有边界直到窗口包含所有需要的字符,然后收缩左边界以找到最小窗口

遍历s字符串中的每个字符,作为答案字符串的起点 left 指针,然后移动 right 指针,便统计是否是有效字符,当有效字符等于t字符串的长度时,right指针就停止移动。记录 right-left+1 的最小值 minLen,以及起始下标 start

当left指针向右移动时,要减去刚刚统计满足的字符,如果此时还满足答案,就更新最小值 minLen 和 start 并继续循环移动left指针 ,如果不满足, 此时right指针只能向右移动,寻找答案 (不能向左,因为向左始终找不到解,从此可以看出满足单调性,适合用滑动窗口的方法来做)

直到left指针和right指针都移动到末尾不能再移动时,结束。

怎么知道是否满足呢,可以使用哈希表的方法

image.png 整合一下可以变成 ht - hs 的哈希表,只要都等于 0 就满足答案

class Solution {
    public String minWindow(String s, String t) {
        //特殊情况特判
        if (s == null || t == null || s.length() < t.length()) {
            return "";
        }
        
        //初始化哈希表,记录需要的字符及其数量
        Map<Character,Integer> target = new HashMap<>();
        for(char c : t.toCharArray()) {
            target.put(c,target.getOrDefault(c,0)+1);
        }
        int left = 0,right = 0; //滑动窗口的左右指针
        int minLen = Integer.MAX_VALUE; // 最小窗口长度
        int minLeft = 0; //最小窗口的起始位置
        int count = t.length(); //还需要匹配的字符数量
        boolean sign = false; //是否找到符合条件的子串
        while(right < s.length()) {
            char c = s.charAt(right);
            //如果当前字符是需要的字符
            if(target.containsKey(c)) {
                if(target.get(c) > 0) {
                    count--;
                }
                target.put(c,target.get(c)-1);
            }
            right++;
            
            //当所有字符都匹配时,尝试收缩做左边界
            while(count == 0) {
                if(right-left < minLen) {
                    sign = true;
                    minLen = right-left;
                    minLeft = left;
                }
                char leftChar = s.charAt(left);
                if(target.containsKey(leftChar)) {
                    target.put(leftChar,target.get(leftChar)+1);
                    if(target.get(leftChar) > 0) {
                        count++;
                    }
                }
                left++;
            }
        }
        String str = "";
        return sign ? s.substring(minLeft,minLen+minLeft) : str; //这里不建议在更新的时候substr,因为复制字符串也有一个O(N)的循环,效率较低
    }
}