最小覆盖子串

时间:2022-07-22
本文章向大家介绍最小覆盖子串,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

问题描述

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

示例:

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

解决方案

题目要求要找到包含T中所有字符的的最小子串,这里的所有字符包括重复字符,也就是说若T中含有两个‘a’,要求的S的子串中也至少含两个‘a’。因此使用一Map存储T中所有字符(key = 字符 value = 出现的次数)。

使用滑动窗口求解,left和right表示窗口的左右边界,为[left, right),其初值均为0。首先让right向右滑动,直到当前窗口中的元素可以覆盖T,然后left也向右滑动,直到不能覆盖T为止,滑动过程中存储最小的子串,如此直到right到达最后一个元素位置。

实现代码如下:

class Solution {
    public String minWindow(String s, String t) {
        Map<Character, Integer> sCount = new HashMap<>();
        Map<Character, Integer> tCount = new HashMap<>();
        char[] arr = s.toCharArray();
        for(int i = 0; i < t.length(); i++){
            tCount.put(t.charAt(i), tCount.getOrDefault(t.charAt(i), 0) + 1);
        }
        // 最小覆盖子串的长度
        int length = s.length() + 1; 
        // 最小覆盖子串开始位置
        int start = 0; 
        // 最小覆盖子串结束位置
        int end = 0; 

        int left = 0;
        int right = 0;
        while(right < arr.length){
            sCount.put(arr[right], sCount.getOrDefault(arr[right], 0) + 1);
            right++;
            while(process(sCount, tCount)){
                if(length > right - left){
                    length = right - left;
                    start = left;
                    end = right;
                }
                sCount.put(arr[left], sCount.get(arr[left]) - 1);
                left++;
            }
        }
        return length == s.length() + 1 ? "" : s.substring(start, end);
    }
    // s是否可以覆盖t
    public boolean process(Map<Character, Integer> sCount, Map<Character, Integer> tCount){
        for(Map.Entry<Character, Integer> entry : tCount.entrySet()){
            if(entry.getValue().intValue() > sCount.getOrDefault(entry.getKey(), 0).intValue()){
                return false;
            }
        }
        return true;
    }
}

时间复杂度为O(N * M),其中N为s的长度,M为t的长度。首先滑动窗口滑动过程时间复杂度为O(N),每一个窗口对于当前窗口能否覆盖t的时间复杂度为O(M),因此总体时间复杂度为O(M * N)。