题目描述
给你一个字符串 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
s 和 t 由英文字母组成
误区:题目中所说意思为:在s中是否包含 t 中的所有字符,即当 s = “a”, t = “aa” 时,返回的应为 “”
遇到的问题:leetcode 76. 最小覆盖子串 使用 java 中的 map 存储字符及对应的次数时,最后一个示例报错:
在 Java 中用Map记录字母出现个数的写法,
最后一个测试用例不能通过:
Integer是对象
Integer会缓存频繁使用的数值
数值范围为-128到127,在此范围内直接返回缓存值。
超过该范围就会new 一个对象。
解决方案为在比较 map 中对应键的值时,使用 equals 判断
代码
/**
使用滑动窗口,设置左右两个指针 right 和 left,滑动窗口为 [left, right) 一个用于「延伸」现有窗口
的 right 指针,和一个用于「收缩」窗口的 left 指针。
在任意时刻,只有一个指针运动,而另一个保持静止。我们在 s 上滑动窗口,通过移动 right 指针不断扩张窗口。
当窗口包含 t 全部所需的字符后,如果能收缩,我们就收缩窗口直到得到最小窗口。
即先寻找可行解(包含 t 中所有字符,再不断优化)
*/
class Solution {
public String minWindow(String s, String t) {
// need 中的键是 t 中的所出现的字符,对应的值为字符出现的次数
// window 中的键是在字符串 s 上查找时,滑动窗口中存在的 t 中的字符,值为该字符在窗口中的个数
if (s == null || s.equals("") || t == null || t.equals("") || s.length() < t.length()) {
return "";
}
Map<Character, Integer> need = new HashMap<>();
Map<Character, Integer> window = new HashMap<>();
for (int i = 0; i < t.length(); i++) {
char c = t.charAt(i);
need.put(c,need.getOrDefault(c, 0) + 1);
}
int left = 0, right = 0;
int valid = 0;
int start = 0, len = 100001;
while (right < s.length()) {
char c = s.charAt(right);
right++;
// 进行窗口内数据的一系列更新
if (need.containsKey(c)) {
window.put(c, window.getOrDefault(c, 0) + 1);
if (window.get(c).equals(need.get(c))) {
valid++;
}
}
// 判断左侧窗口是否需要收缩
while (valid == need.size()) {
// 更新最小覆盖子串
if (right - left < len) {
start = left;
len = right - left;
}
// d 是将移出窗口的字符
char d = s.charAt(left);
left++;
// 对窗口内数据进行一系列更新
if (need.containsKey(d)) {
if (window.get(c).equals(need.get(c)))
valid--;
window.put(d, window.get(d) - 1);
}
}
}
return len == 100001 ? "" : s.substring(start, len + start);
}
}
附:滑动窗口算法框架
void slideWindow(String s, String t) {
Map<Character, Integer> need = new HashMap<>();
Map<Character, Integer> window = new HashMap<>();
for (int i = 0; i < t.length(); i++) {
char c = t.charAt(i);
need.put(c, need.getOrDefault(c, 0) + 1);
}
int left = 0, right = 0;
int valid = 0;
while (right < s.length()) {
// c是将移入窗口的字符
char c = s.charAt(right);
// 右移窗口
right++;
// 进行窗口内数据的一系列更新
...
/******* debug 输出位置 *******/
System.out.format("window:[ %d, %d)", left, right);
// 判断左侧窗口是否要收缩
/**
while (window needs shrink) {
// d 是将溢出窗口的字符
char d = s.charAt(left);
// 左移窗口
left++;
// 进行窗口内数据的一系列更新
...
}
*/
}
}