题目
给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 "" 。
注意:
- 对于
t中重复字符,我们寻找的子字符串中该字符数量必须不少于t中该字符数量。 - 如果
s中存在这样的子串,我们保证它是唯一的答案。
示例 1:
输入: s = "ADOBECODEBANC", t = "ABC"
输出: "BANC"
解释: 最小覆盖子串 "BANC" 包含来自字符串 t 的 'A'、'B' 和 'C'。
题解
方式一:滑动窗口 + 哈希表
// 记录目标字符及数量
HashMap<Character, Integer> target = new HashMap<>();
// 记录当前窗口包含的字符及数量
HashMap<Character, Integer> cur = new HashMap<>();
public String minWindow(String s, String t) {
for (char c: t.toCharArray()) {
target.put(c, target.getOrDefault(c, 0) + 1);
}
int start = 0, end = 0, l = 0, minLen = Integer.MAX_VALUE;
for (int r = 0; r < s.length(); r++) {
char c = s.charAt(r);
// 只记录target中存在的字符
if (target.containsKey(c)) {
cur.put(c, cur.getOrDefault(c, 0) + 1);
}
while (l <= r && check()) {
if (r - l + 1 < minLen) {
minLen = r - l + 1;
start = l;
end = l + minLen;
}
char cc = s.charAt(l);
if (target.containsKey(cc)) {
cur.put(cc, cur.getOrDefault(cc, 0) - 1);
}
l++;
}
}
return s.substring(start, end);
}
boolean check() {
// 和forEach相比可以提前结束
Iterator iter = target.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry entry = (Map.Entry) iter.next();
Character key = (Character) entry.getKey();
Integer val = (Integer) entry.getValue();
if (cur.getOrDefault(key, 0) < val) {
return false;
}
}
return true;
}
方式二:滑动窗口 + 数组
A = 65 , z = 122
int[] target = new int[58];
int[] cur = new int[58];
public String minWindow(String s, String t) {
for (char c: t.toCharArray()) {
target[c - 'A']++;
}
int start = 0, end = 0, l = 0, minLen = Integer.MAX_VALUE;
for (int r = 0; r < s.length(); r++) {
char c = s.charAt(r);
cur[c - 'A']++;
while (l <= r && check()) {
if (r - l + 1 < minLen) {
minLen = r - l + 1;
start = l;
end = l + minLen;
}
char cc = s.charAt(l);
cur[cc - 'A']--;
l++;
}
}
return s.substring(start, end);
}
boolean check() {
for (int i = 0; i < target.length; i++) {
if (target[i] > 0 && cur[i] < target[i]) {
return false;
}
}
return true;
}
总结
算法:滑动窗口
数据结构:哈希表、数组