给你一个字符串 s
、一个字符串 t
。返回 s
中涵盖 t
所有字符的最小子串。如果 s
中不存在涵盖 t
所有字符的子串,则返回空字符串 ""
。
注意:
- 对于
t
中重复字符,我们寻找的子字符串中该字符数量必须不少于t
中该字符数量。 - 如果
s
中存在这样的子串,我们保证它是唯一的答案
提示:
m == s.length
n == t.length
1 <= m, n <= 10^5
s
和t
由英文字母组成
解:使用滑动窗口和哈希表,首先记录 t 字符串中的字符出现次数,建立left和right指针,right指针遍历 s 字符串并统计 s 串的字符出现字数,并且判断是否[left, right]区间是否涵盖 t 字符串,如果涵盖,进行子串长度比较(right - left < ansLeft - ansRight),如果比之前的子串小就赋值并且移动 left 指针以此往复直到不涵盖 t 字符串。(是否涵盖t字符串判断能继续优化)
思路:灵茶山艾府
class Solution {
public String minWindow(String s, String t) {
int ansLeft = -1;
int ansRight = s.length();
int left = 0;
int right;
Map<Character, Integer> cntS = new HashMap<>();
Map<Character, Integer> cntT = new HashMap<>();
for (int i = 0; i < t.length(); i++) {
cntT.merge(t.charAt(i), 1, Integer::sum);
}
for (right = 0; right < s.length(); right++) {
cntS.merge(s.charAt(right), 1, Integer::sum);
while (isCovered(cntS, cntT)) {
if (right - left < ansRight - ansLeft) {
ansLeft = left;
ansRight = right;
}
char ch = s.charAt(left++);
cntS.put(ch, cntS.get(ch) - 1); // 移动左端点并移除
}
}
return ansLeft < 0 ? "" : s.substring(ansLeft, ansRight + 1);
}
private boolean isCovered(Map<Character, Integer> cntS, Map<Character, Integer> cntT) {
for (Character ch : cntT.keySet()) {
if (cntS.getOrDefault(ch, 0) < cntT.getOrDefault(ch, 0)) {
return false;
}
}
return true;
}
}