最小覆盖子串

76. 最小覆盖子串

给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 ""

注意:

  • 对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。
  • 如果 s 中存在这样的子串,我们保证它是唯一的答案。

示例 1:

1
2
3
输入:s = "ADOBECODEBANC", t = "ABC"
输出:"BANC"
解释:最小覆盖子串 "BANC" 包含来自字符串 t 的 'A'、'B' 和 'C'。

示例 2:

1
2
3
输入:s = "a", t = "a"
输出:"a"
解释:整个字符串 s 是最小覆盖子串。

示例 3:

1
2
3
4
输入: s = "a", t = "aa"
输出: ""
解释: t 中两个字符 'a' 均应包含在 s 的子串中,
因此没有符合条件的子字符串,返回空字符串。

提示:

  • m == s.length
  • n == t.length
  • 1 <= m, n <= 105
  • st 由英文字母组成

解题思路

利用滑动窗口的思想

用一个map存储t中出现的词频,为正数则表示还需要多少个字符,为负数则表示冗余了多少字符

通过right指针不断增加找到所需要的全部字符,我们使用count来记录需要字符的种类,当count==0,表明此时已经找到全部字符

找到全部字符后,进行收缩窗口,left指针移动,移除元素

如果移除的元素是需要的字符种类,则词频加1,但词频+1不一定需要移动right来获取新的字符,例如:t = “ABC”,而此时窗口的字符串为”AABC”,移除A后变为”ABC”。

因此需要判断移除元素后,map的值是否大于0

  • 若大于0,则需要继续移动right指针获取元素

  • 若小于等于0,则可以继续移动left缩减区间

代码

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
class Solution {

public String minWindow(String s, String t) {
Map<Character, Integer> map = new HashMap<>(); //保存需要的字符及词频

for(int i = 0; i < t.length(); i++){
char c = t.charAt(i);
map.put(c, map.getOrDefault(c, 0) + 1);
}
int count = map.size(); //字符的种类的个数

int left = 0;
int right = 0;
int min_len = Integer.MAX_VALUE;
String min_str = "";

while(right < s.length()){
char right_ch = s.charAt(right);
if(map.containsKey(right_ch)){//找到了需要的字符
map.put(right_ch, map.get(right_ch)-1); //找到了词频减1
if(map.get(right_ch) == 0){ //词频减1后为0
count --; //表示不需要该元素的种类
}

while(count == 0){ //所有的字符都找到了
int sublen = right - left + 1; //字串长度
if(sublen < min_len){
min_len = sublen;
min_str = s.substring(left, right + 1);
}
//尝试缩小窗口
char left_ch = s.charAt(left);
if(map.containsKey(left_ch)){ //如果移除的元素正好需要,词频+1
map.put(left_ch, map.get(left_ch) + 1);
if(map.get(left_ch) == 1){ //移除字符后词频大于0
count ++; //表示还需要该类元素
}
}
left++;//移除左边元素
}//while
}
right++;
}
return min_str;
}
}

最小覆盖子串
http://example.com/2023/05/22/算法/滑动窗口,双指针/3. 最小覆盖子串/
作者
PALE13
发布于
2023年5月22日
许可协议