关键算法知识点: 滑动窗口(双指针) + 哈希表
做题思路:
扩展有边界直到窗口包含所有需要的字符,然后收缩左边界以找到最小窗口
遍历s字符串中的每个字符,作为答案字符串的起点 left 指针,然后移动 right 指针,便统计是否是有效字符,当有效字符等于t字符串的长度时,right指针就停止移动。记录 right-left+1 的最小值 minLen,以及起始下标 start
当left指针向右移动时,要减去刚刚统计满足的字符,如果此时还满足答案,就更新最小值 minLen 和 start 并继续循环移动left指针 ,如果不满足, 此时right指针只能向右移动,寻找答案 (不能向左,因为向左始终找不到解,从此可以看出满足单调性,适合用滑动窗口的方法来做)
直到left指针和right指针都移动到末尾不能再移动时,结束。
怎么知道是否满足呢,可以使用哈希表的方法
整合一下可以变成 ht - hs 的哈希表,只要都等于 0 就满足答案
class Solution {
public String minWindow(String s, String t) {
//特殊情况特判
if (s == null || t == null || s.length() < t.length()) {
return "";
}
//初始化哈希表,记录需要的字符及其数量
Map<Character,Integer> target = new HashMap<>();
for(char c : t.toCharArray()) {
target.put(c,target.getOrDefault(c,0)+1);
}
int left = 0,right = 0; //滑动窗口的左右指针
int minLen = Integer.MAX_VALUE; // 最小窗口长度
int minLeft = 0; //最小窗口的起始位置
int count = t.length(); //还需要匹配的字符数量
boolean sign = false; //是否找到符合条件的子串
while(right < s.length()) {
char c = s.charAt(right);
//如果当前字符是需要的字符
if(target.containsKey(c)) {
if(target.get(c) > 0) {
count--;
}
target.put(c,target.get(c)-1);
}
right++;
//当所有字符都匹配时,尝试收缩做左边界
while(count == 0) {
if(right-left < minLen) {
sign = true;
minLen = right-left;
minLeft = left;
}
char leftChar = s.charAt(left);
if(target.containsKey(leftChar)) {
target.put(leftChar,target.get(leftChar)+1);
if(target.get(leftChar) > 0) {
count++;
}
}
left++;
}
}
String str = "";
return sign ? s.substring(minLeft,minLen+minLeft) : str; //这里不建议在更新的时候substr,因为复制字符串也有一个O(N)的循环,效率较低
}
}