题目
给定两个字符串 s
和 t
。返回 s
中包含 t
的所有字符的最短子字符串。如果 s
中不存在符合条件的子字符串,则返回空字符串 ""
。
如果 s
中存在多个符合条件的子字符串,返回任意一个。
注意: 对于 t
中重复字符,我们寻找的子字符串中该字符数量必须不少于 t
中该字符数量。
示例 1:
输入:s = "ADOBECODEBANC", t = "ABC"
输出:"BANC"
解释:最短子字符串 "BANC" 包含了字符串 t 的所有字符 'A'、'B'、'C'
1
2
3
2
3
示例 2:
输入:s = "a", t = "a"
输出:"a"
1
2
2
示例 3:
输入:s = "a", t = "aa"
输出:""
解释:t 中两个字符 'a' 均应包含在 s 的子串中,因此没有符合条件的子字符串,返回空字符串。
1
2
3
2
3
提示:
1 <= s.length, t.length <= 10
5s
和t
由英文字母组成
进阶: 你能设计一个在 o(n)
时间内解决此问题的算法吗?
注意:本题与主站 76 题相似(本题答案不唯一):https://leetcode-cn.com/problems/minimum-window-substring/
题解
java
public String minWindow(String s, String t) {
int len = s.length();
// 待匹配的字符及数量
Map<Character, Integer> map = new HashMap<>();
// 待匹配的所有字符个数
for (char c : t.toCharArray()) {
map.merge(c, 1, Integer::sum);
}
char c;
String result = "";
int[] cnt = new int[1];
int left = 0, right = 0, need = map.size();
while (right < len) {
c = s.charAt(right);
map.computeIfPresent(c, (k, v) -> {
// 刚好是从1到0 则表示k这个字符已经匹配完成 即这个字符待匹配数量从 1 -> 0
if (Objects.equals(v, 1)) {
// 匹配完成字符数量增加
cnt[0]++;
}
return v - 1;
});
// 如果已经匹配完成 从左向右滑动窗口
while (cnt[0] == need) {
// 匹配完成的字符串比较 缓存最短字符串
if (result.isEmpty() || (right - left + 1) < result.length()) {
result = s.substring(left, right + 1);
}
// 收缩左边界
map.computeIfPresent(s.charAt(left), (k, v) -> {
// 刚好恢复一个匹配完成的字符 即这个字符待匹配数量从0 -> 1
if (Objects.equals(v, 0)) {
// 匹配完成字符数量减少
cnt[0]--;
}
return v + 1;
});
left++;
}
right++;
}
return result;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52