76. 最小覆盖子串
给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 “” 。
注意:
对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。
如果 s 中存在这样的子串,我们保证它是唯一的答案。
示例 1:
输入:s = "ADOBECODEBANC", t = "ABC"
输出:"BANC"
示例 2:
输入:s = "a", t = "a"
输出:"a"
示例 3:
输入: s = "a", t = "aa"
输出: ""
解释: t 中两个字符 ‘a’ 均应包含在 s 的子串中,
因此没有符合条件的子字符串,返回空字符串。
题解:
使用滑动窗口
class Solution {
public String minWindow(String s, String t) {
Map<Character, Integer> tmap = new HashMap<>();
// 计算tmap中每个字符的数量
for (int i=0; i<t.length(); i++) {
update(tmap, t.charAt(i), 1);
}
// 定义滑动窗口计算
int minSize = Integer.MAX_VALUE; // 最小窗口大小
int minL = -1; // 最小左指针
int minR = -1; // 最下有指针
// 滑动窗口初始化
int l=0;
int r=-1;
int n = s.length();
Map<Character, Integer> smap = new HashMap<>(); // 保存滑动窗口的字符的数量
while (l<n && r<n) {
// 如果没有覆盖
if (!cover(smap, tmap)) {
r++; // 滑动窗口变大
if (r > n-1) break; // 如果r大于n-1,直接跳出循环,不需要在直行下面的语句了
update(smap, s.charAt(r), 1); // 更新当前字符的数据+1
// 如果覆盖
} else {
// 最小窗口大小重新赋值
if (minSize > (r-l+1)) {
minSize = r-l+1;
minL = l;
minR = r;
}
update(smap, s.charAt(l), -1); // 当前字符的数量-1
l++; // 左指针右移
}
}
if (minL == -1) {
return "";
}
return s.substring(minL, minR+1);
}
// smap中是否包含所有的tmap
public boolean cover(Map<Character, Integer> smap, Map<Character, Integer> tmap) {
for (Map.Entry<Character, Integer> entry : tmap.entrySet()) {
if (!smap.containsKey(entry.getKey())) return false;
if (smap.get(entry.getKey()) < entry.getValue()) return false; // 注意这里是小于
}
return true;
}
// 计算tMap中字符的数量
public void update(Map<Character, Integer> tmap, Character key, int count) {
if (tmap.containsKey(key)) {
count = tmap.get(key) + count;
}
tmap.put(key, count);
}
}