来源:力扣(LeetCode)
题目链接:https://leetcode.cn/problems/minimum-window-substring
难度:困难
给你一个字符串 s
、一个字符串 t
。返回 s
中涵盖 t
所有字符的最小子串。如果 s
中不存在涵盖 t
所有字符的子串,则返回空字符串 ""
。
注意:
t
中重复字符,我们寻找的子字符串中该字符数量必须不少于 t
中该字符数量。s
中存在这样的子串,我们保证它是唯一的答案。示例 1:
输入:s = "ADOBECODEBANC", t = "ABC"
输出:"BANC"
示例 2:
输入:s = "a", t = "a"
输出:"a"
示例 3:
输入: s = "a", t = "aa"
输出: ""
解释: t 中两个字符 'a' 均应包含在 s 的子串中,
因此没有符合条件的子字符串,返回空字符串。
提示:
1 <= s.length, t.length <= 105
s
和 t
由英文字母组成进阶: 你能设计一个在 o(n)
时间内解决此问题的算法吗?
第一次没看答案做出的困难题
对于滑动窗口的解释来源于LeetCode官方
作者:LeetCode-Solution
链接:https://leetcode.cn/problems/minimum-window-substring/solution/zui-xiao-fu-gai-zi-chuan-by-leetcode-solution/
来源:力扣(LeetCode)
本问题要求我们返回字符串 ss 中包含字符串 tt 的全部字符的最小窗口。我们称包含 tt 的全部字母的窗口为「可行」窗口。
我们可以用滑动窗口的思想解决这个问题。在滑动窗口类型的问题中都会有两个指针,一个用于「延伸」现有窗口的 rr 指针,和一个用于「收缩」窗口的 ll 指针。在任意时刻,只有一个指针运动,而另一个保持静止。我们在 s 上滑动窗口,通过移动 r 指针不断扩张窗口。当窗口包含 t 全部所需的字符后,如果能收缩,我们就收缩窗口直到得到最小窗口。
以上是LeetCode官方对滑动窗口方法的解释,下面说一说该题具体操作。
其解法就是使用滑动窗口,但是如何判断对于s字符串的下标 i到下标 j 之间的子字符串
已经包含了字符串t
呢?我们可以使用两个map来分别记录字符串s的子字符串
各个字符的个数和字符串t
各个字符的个数,只要字符串s的子字符串
包含字符串t
的各个字符且各个字符的数量大于等于字符串t
的各个字符,则包含。
class Solution {
public String minWindow(String s, String t) {
//字符串s的起始下标
int i = 0;
//用于记录字符串t的各个字符个数
HashMap<Character,Integer> count = new HashMap<>();
setCount(t,count);
//用于记录字符串s的子字符串各个字符的个数
HashMap<Character,Integer> result = new HashMap<>();
String ans = "";
//用于包含字符串t的字符串s的子串的长度
int len = Integer.MAX_VALUE;
//j是字符串s的终点下标
for(int j = 0; j < s.length(); j++) {
//记录字符串s各个字符的个数
result.put(s.charAt(j), result.containsKey(s.charAt(j)) ? result.get(s.charAt(j)) + 1 : 1);
//当字符串s的子串包含字符串t
while(isCover(count, result)) {
//如果之前的包含字符串t的字符串s的子串的长度大于当前子串的长度
if(len > j - i + 1) {
//更改最短子串的长度
len = j - i + 1;
//更改最短字符串
ans = s.substring(i,j + 1);
}
//丢弃当前起始下标i的字符
result.put(s.charAt(i), result.get(s.charAt(i)) - 1);
//如果起始下标i的字符个数等于0
if(result.get(s.charAt(i)) == 0) {
//删除该字符的记录
result.remove(s.charAt(i));
}
//字符串s的起始下标往前移动一位
i++;
}
}
return ans;
}
//用于判断字符串s的子字符串是否包含了字符串t
public static boolean isCover(Map<Character,Integer> count, Map<Character,Integer> result) {
for(Character key : count.keySet()) {
if(!result.containsKey(key)) {
return false;
} else if(result.get(key) < count.get(key)) {
return false;
}
}
return true;
}
//计算字符串t各个字符的个数
public static void setCount(String t, Map<Character,Integer> count) {
for(int i = 0; i < t.length(); i++) {
count.put(t.charAt(i), count.containsKey(t.charAt(i)) ? count.get(t.charAt(i)) + 1 : 1);
}
}
}