「力扣」第 76 题:最小覆盖子串(滑动窗口)

  • 只关心字符是否出现,并不关心字符出现的次数。

滑动窗口的定义:表示滑动窗口内部包含了 t 中的字符个数,这里借用了编辑距离的概念。

方法一:定义距离

Java 代码:

public class Solution {

    public String minWindow(String s, String t) {
        int[] window = new int[128];
        int[] pattern = new int[128];

        final int A = 'A';

        char[] tCharArray = t.toCharArray();
        for (Character c : tCharArray) {
            pattern[c - A]++;
        }

        // 表示滑动窗口内部包含了 t 中的字符个数,这里借用了编辑距离的概念
        int distance = 0;

        for (int i = 0; i < 128; i++) {
            if (pattern[i] > 0) {
                distance++;
            }
        }

        int sLen = s.length();
        int start = 0;
        int left = 0;
        int right = 0;
        int match = 0;
        int minLen = sLen + 1;

        char[] sCharArray = s.toCharArray();

        // 滑动窗口定义:[left, right) 是包含 T 的子串

        while (right < sLen) {
            Character curChar = sCharArray[right];
            if (pattern[curChar - A] > 0) {
                window[curChar - A]++;

                if (window[curChar - A] == pattern[curChar - A]) {
                    match++;
                }
            }

            right++;

            while (match == distance) {
                if (right - left < minLen) {
                    start = left;
                    minLen = right - left;
                }

                // 考虑左边界向右边走
                Character leftChar = sCharArray[left];
                if (pattern[leftChar - A] > 0) {
                    window[leftChar - A]--;

                    if (window[leftChar - A] < pattern[leftChar - A]) {
                        match--;
                    }
                }
                left++;
            }
        }

        if (minLen == sLen + 1) {
            return "";
        }
        return s.substring(start, start + minLen);
    }

    public static void main(String[] args) {
        Solution solution = new Solution();
        String S = "ADOBECODEBANC";
        String T = "ABC";
        String minWindow = solution.minWindow(S, T);
        System.out.println(minWindow);
    }
}

方法二:定义字符数是否一样

滑动窗口方法是暴力解法的优化,窗口在滑动的过程中,不会错过最优解。

  • 右边界可以前进的条件:还没有包含 T 的所有字母;
  • 左边界可以前进的条件:已经包含了 T 的所有字母;
  • 滑动窗口定义:[left, right) 内包含了 T 的所有字母,长度为 right - left

Java 代码:

public class Solution {

    /**
     * 首先建立文本串和模式串的概念
     *
     * @param s 文本串
     * @param t 模式串
     * @return
     */
    public String minWindow(String s, String t) {
        int[] pattern = new int[128];
        int[] window = new int[128];
        for (char ct : t.toCharArray()) {
            pattern[ct]++;
        }
        // t 中有多少种字符,重复字符只记录一次
        int tCount = 0;
        // 滑动窗口内有多少种字符在 t 中
        int sCount = 0;
        // 首先计算滑动窗口内的元素和 pattern 的差距
        for (int i = 0; i < 128; i++) {
            if (pattern[i] > 0) {
                tCount++;
            }
        }
        
        int sLen = s.length();
        int start = 0;
        int left = 0;
        int right = 0;
        int minLen = sLen + 1;

        char[] sCharArray = s.toCharArray();
        while (right < sLen) {
            if (pattern[sCharArray[right]] > 0) {
                window[sCharArray[right]]++;
                if (window[sCharArray[right]] == pattern[sCharArray[right]]) {
                    sCount++;
                }
            }

            right++;
            while (sCount == tCount) {
                // 想清楚为什么在这里取最小值,此时 [left, right) 内正好包含 T 的所有字母,这一段要写在 left 移动之前
                if (right - left < minLen) {
                    minLen = right - left;
                    start = left;
                }
                if (pattern[s.charAt(left)] > 0) {
                    window[s.charAt(left)]--;
                    if (window[s.charAt(left)] < pattern[s.charAt(left)]) {
                        sCount--;
                    }
                }
                left++;
            }
        }
        if (minLen == sLen + 1) {
            return "";
        }
        return s.substring(start, start + minLen);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值