题目
给你一个字符串 S、一个字符串 T,请在字符串 S 里面找出:包含 T 所有字母的最小子串。
示例:
输入: S = “ADOBECODEBANC”, T = “ABC”
输出: “BANC”
说明:
- 如果 S 中不存这样的子串,则返回空字符串 “”。
- 如果 S 中存在这样的子串,我们保证它是唯一的答案。
解答
使用滑动窗口算法思想。
滑动窗口问题通用模板如下(本文章的重点内容!)
int left = 0, right = 0;
while (right < s.size()) {
window.add(s[right]);
right++;
while (valid) {
window.remove(s[left]);
left++;
}
}
本题步骤如下:
- 先将 t 字符串的所有字符存入哈希表。
- 声明 lt, gt 两个指针作为滑动窗口 [lt, gt - 1] 的边界
- 此时如果窗口还不能匹配 t 的窗口, 让窗口扩大,gt ++ 。
- 若能匹配 t 的窗口, 统计最小子串,然后使窗口缩小,不断地让左边界 lt ++ ,直到不再覆盖 t 字串。
tips:本题优化成 int[128] 代替 哈希表 效率会很高,但不太通用,我认为此优化学习不到啥,所以我没做。
复杂度:O(S + T) 时间,O(S + T) 空间。
本题代码
class Solution {
public String minWindow(String s, String t) {
char[] ts = t.toCharArray();
Map<Character, Integer> needs = new HashMap<>();
for(char ch : ts) needs.put(ch, needs.getOrDefault(ch, 0) + 1);
Map<Character, Integer> window = new HashMap<>();
int lt = 0; int gt = 0;
int start = 0; int end = s.length() + 1;
int match = 0;
while(gt < s.length()) {
char c1 = s.charAt(gt);
if(needs.containsKey(c1)) {
window.put(c1, window.getOrDefault(c1, 0) + 1);
if(window.get(c1).equals(needs.get(c1))) match ++;
}
gt ++;
while(match == needs.size()) {
if(gt - lt < end - start) {
start = lt;
end = gt;
}
char c2 = s.charAt(lt);
if(needs.containsKey(c2)) {
window.put(c2, window.get(c2) - 1);
if(window.get(c2) < needs.get(c2)) match --;
}
lt ++;
}
}
return end == s.length() + 1 ? "" : s.substring(start, end);
}
}