题目描述
给你一个字符串 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)
}