76. 最小覆盖子串
hard
给你一个字符串 s
、一个字符串 t
。返回 s
中涵盖 t
所有字符的最小子串。如果 s
中不存在涵盖 t
所有字符的子串,则返回空字符串 ""
。
注意:
- 对于
t
中重复字符,我们寻找的子字符串中该字符数量必须不少于 t
中该字符数量。 - 如果
s
中存在这样的子串,我们保证它是唯一的答案。
示例 1:
1
2
| 输入:s = "ADOBECODEBANC", t = "ABC"
输出:"BANC"
|
示例 2:
1
2
| 输入:s = "a", t = "a"
输出:"a"
|
示例 3:
1
2
3
4
| 输入: s = "a", t = "aa"
输出: ""
解释: t 中两个字符 'a' 均应包含在 s 的子串中,
因此没有符合条件的子字符串,返回空字符串。
|
滑动窗口
典型的滑动窗口思路,窗口扩展时寻找可行解,窗口收缩时优化可行解。
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
| func minWindow(s string, t string) string {
if s == t {
return s
}
hash := make(map[byte]int) // 记录目标字母及其个数 作为需求
for i := 0; i < len(t); i++ {
hash[t[i]]++
}
reL, reR := 0, -1 // 目标的左右边界
minLen := math.MaxInt
left, right := 0, 0
sum := len(t) // 记录总数 为0时代表全命中
for right < len(s) {
if _, ok := hash[s[right]]; ok {
// 存在
hash[s[right]]--
if hash[s[right]] >= 0 {
// 变成负数时sum不用减少 因为已经满足
sum--
}
}
if sum == 0 {
// 全部命中
for left <= right {
// left右移 直到不满足全命中
if _, ok := hash[s[left]]; ok {
hash[s[left]]++
if hash[s[left]] > 0 {
// 需求得不到满足了
if minLen > right-left+1 {
// 刷新最短值
reR, reL = right, left
minLen = right - left + 1
}
sum++
left++ // 正式地++ 破坏全命中状态
break
}
}
left++ // 注意 先判断再++
}
}
right++
}
return s[reL : reR+1]
}
|