思路:
用滑动窗口的思路。
建立一个min=max_length保存最小字串长度。建立两个指针left 和 right ,一开始都指向字符串开头。然后用一个hashmap (名为window)保存窗口内容(字符—>该字符在窗口内出现的次数)。用一个hashmap保存t内容。
进入while循环,当right大于s.length()-1时,退出循环。
- 首先判断窗口内是否刚好涵盖了t(t中的字符是否在窗口内而且还需要t中的每一个字符出现的次数与窗口内对应字符出现的次数相等)。
- 如果没有涵盖t,则right右移一位。然后进入下一次循环。
- 如果窗口正好涵盖了t (注意正好涵盖了t说明窗口内也包含t中所有字符且对应字符出现的数量正好相等,不能多),这时么我们就可以考虑右移left来缩减窗口的大小,来找到那个涵盖了t的最小子串。当left 的左移是将窗口内的元素从左到右逐渐移出的过程。每次我们右移left的时候都看一看这个left指向的元素是不是在 t 中,如果我们发现本次要移出的元素正好被 t 包含,那么说明从当前left为起点,到right所指向的位置就是s中的某一个涵盖t所有字符的最小字串,我们记录right-left+1为当前子串长度,并更新min。当right大于s.length()-1时,退出循环。
最后返回min。
class Solution {
public String minWindow(String s, String t) {
int n = s.length();
HashMap<Character, Integer> tMap = new HashMap<>();
HashMap<Character, Integer> windowMap = new HashMap<>();
for (char c : t.toCharArray()) { // 记录 t 中所有字符出现的次数
tMap.put(c, tMap.getOrDefault(c, 0) + 1);
}
int left = 0, right = 0;
// 记录窗口中满足条件的字符个数
int flag = 0;
// 记录最小覆盖字串的起始索引及长度
int start = 0, minLength = Integer.MAX_VALUE;
while (right < n) {
char c = s.charAt(right);
// 判断取出的字符是否在 t 中
if (tMap.containsKey(c)) {
windowMap.put(c, windowMap.getOrDefault(c, 0) + 1);
// 判断取出的字符在窗口中的出现次数是否与 t 中该字符的出现次数相同
if (windowMap.get(c).equals(tMap.get(c))) {
flag++;
}
}
// 判断是否需要缩小窗口(已经找到符合条件的子串)
while (flag == tMap.size()) {
if (right - left + 1 < minLength) {
start = left;
minLength = right - left + 1;
}
char c1 = s.charAt(left);
left++;
if (tMap.containsKey(c1)) {
if (windowMap.get(c1).equals(tMap.get(c1))) {
flag--;
}
windowMap.put(c1, windowMap.getOrDefault(c1, 0) - 1);
}
}
right++;
}
return minLength == Integer.MAX_VALUE ? "" : s.substring(start, start + minLength);
}
}