LeetCode 76 – 最小覆盖子串

题目描述

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

注意:

对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。
如果 s 中存在这样的子串,我们保证它是唯一的答案。

示例 1:

输入:s = “ADOBECODEBANC”, t = “ABC”
输出:”BANC”
解释:最小覆盖子串 “BANC” 包含来自字符串 t 的 ‘A’、’B’ 和 ‘C’。

题目分析

双指针,滑动窗口
使用一个数组表示窗口相对于 t 各字符的个数,缺少为负数,多余为正数
如何判断窗口内是否已经包含 t 所有字符?没必要每次都遍历检查数组是否全为0
设置一个计数器 count 即可,表示剩余需要匹配的字符,初始值为 t.length
当窗口中增加或删除字符时,通过数组是否为负数,可以判断这个字符是不是必要的,从而维护 count。
– count > 0 时,说明当前窗口还不够,将窗口向右扩展一位
– count <= 0 时,说明窗口已经足够大了,将窗口左边缩小一位

时间复杂度 O(m + n)

Java

public String minWindow(String s, String t) {
    int left = 0, right = -1;
    int ansL = -1, ansR = -1;
    int[] num = new int['z' - 'A' + 1];
    int count = t.length();
    for (int i = 0; i < t.length(); i++) {
        num[t.charAt(i) - 'A']--;
    }

    while (right < s.length()) {
        if (count > 0) {
            right++;
            if (right < s.length()) {
                if (num[s.charAt(right) - 'A'] < 0) {
                    count--;
                }
                num[s.charAt(right) - 'A']++;
            }
        } else {
            if (right - left < ansR - ansL || ansL == -1) {
                ansL = left;
                ansR = right;
            }
            num[s.charAt(left) - 'A']--;
            if (num[s.charAt(left) - 'A'] < 0) {
                count++;
            }
            left++;
        }
    }
    if (ansL == -1) {
        return "";
    } else {
        return s.substring(ansL, ansR + 1);
    }
}

Kotlin

fun minWindow(s: String, t: String): String {
    val size = 'z' - 'A' + 1
    val num = IntArray(size)
    for (ch in t) {
        num[ch - 'A']--
    }
    // 前闭后闭
    var left = 0
    var right = -1
    var start = -1
    var end = -1
    val length = s.length
    var count = t.length // 表示剩余需要匹配的字符
    while (right < length) {
        if (count > 0) {
            // 增加右边的
            right++
            if (right < length) {
                if (num[s[right] - 'A'] < 0) {
                    count--
                }
                num[s[right] - 'A']++
            }
        } else {
            if (right - left < end - start || start < 0) {
                start = left
                end = right
            }

            // 删除左边的
            num[s[left] - 'A']--
            if (num[s[left] - 'A'] < 0) {
                count++
            }
            left++
        }
    }
    return if (start == -1) "" else s.substring(start, end + 1)
}

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部