• Hard | LeetCode 76. 最小覆盖子串 | 滑动窗口


    76. 最小覆盖子串

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

    注意:如果 s 中存在这样的子串,我们保证它是唯一的答案。

    示例 1:

    输入:s = "ADOBECODEBANC", t = "ABC"
    输出:"BANC"
    

    示例 2:

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

    提示:

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

    进阶:你能设计一个在 o(n) 时间内解决此问题的算法吗?

    解体思路

    方法一: 滑动窗口

    思路很容易想到。首先设置两个指针left, right, 分别代表滑动窗口的左右边界, 初始时指向为0。首先先让右边界逐渐向右移动, 直到滑动窗口内部已经包含了T中的所有字符。

    在滑动窗口内部已经包含了T中所有字符过后, 让左边界逐渐向右移动, 尽可能得缩小滑动滑动大小。最小滑动窗口的大小总是在移动左边界的过程中得出。

    在恰好不能包含T中所有字符时, 可以对最小长度进行更新。并且记录下此时窗口的起始位置和长度。

    在判断滑动窗口内字符是否包含了T中所有字符时, 有一个小小的技巧。

    设置一个变量distance表示窗口内字符里T中的字符出现的个数。对T中某个字符, 窗口内字符出现的个数最大为T中此字符个数。

    比如窗口字符为aaabbbcccc, 而T字符为aabbc, 那么对于窗口的字符统计为winFreq['a'] = 3, winFreq['b'] = 3, winFreq['c'] = 3, 距离distance = 3

    这样, distance最大为T字符串的长度。当distance == tLen时, 代表此时滑动窗口已经包含了所有字符(但是winFreq内的字符统计, 可能会比T串字符出现的个数多, 这是允许的)。

    同理在左边界逐渐向右移动, 恰好不包含T中字符时, 也是同样的方法, 当某个字符窗口个数和T串个数恰好相等时, 如果左边界继续右移, 则窗口将不再包含T中所有字符。此时需要将distance--。

    public String minWindow(String s, String t) {
        int sLen = s.length(), tLen = t.length();
        char[] ss = s.toCharArray();
        char[] tt = t.toCharArray();
        // winFreq, tFreq分别用于统计滑动窗口内部和t串内字符出现次数
        int[] tFreq = new int[128];
        int[] winFreq = new int[128];
        for (char c : tt) {
            tFreq[c]++;
        }
        int left = 0, right = 0;
        // distance表示在s字符串中,t中字符出现的个数
        // 对单个字符而言, 最多出现t中字符串出现的次数
        int distance = 0;
        // 最小子串的长度
        int minLen = Integer.MAX_VALUE;
        int resBegin = 0;
        while (right < sLen) {
            if (tFreq[ss[right]] > 0) {
                // 如果右边界字符是t中出现的字符
                if (winFreq[ss[right]] < tFreq[ss[right]]) {
                    // 计数距离自增
                    distance++;
                }
                // 滑动窗口内对此字符计数
                winFreq[ss[right]]++;
    
                while (distance == tLen) {
                    // distance == tLen 表示滑动窗口当中已经包含了全部T中的字符
                    // 此时需要左窗口不断向右滑动
                    if (tFreq[ss[left]] > 0) {
                        // 左边界的字符在滑动窗口出现,
                        if (winFreq[ss[left]] == tFreq[ss[left]]) {
                            if (minLen > right - left + 1) {
                                // 需要更新长度
                                minLen = right - left + 1;
                                resBegin = left;
                            }
                            distance--;
                        }
                        // 修改次数
                        winFreq[ss[left]]--;
                    }
                    left++;
                }
    
            }
            right++;
        }
        if (minLen == Integer.MAX_VALUE) {
            // S中没有包含T的最小覆盖子串
            return "";
        }
    
        return s.substring(resBegin, resBegin + minLen);
    }
    
  • 相关阅读:
    asp.net 下载EXCEL文件
    C# 泛型实现Table与实体的相互转换
    SQL处理数据并发,解决ID自增
    html Canvas 画图 能够选择并能移动
    AJAX get/post;
    (转) C# Async与Await的使用
    FormattableString 取代特定区域字符串
    少用 string.Format
    少用 string.Format
    如何重写object虚方法
  • 原文地址:https://www.cnblogs.com/chenrj97/p/14747911.html
Copyright © 2020-2023  润新知