LeetCode-76. 最小覆盖子串
作者:互联网
题目来源
题目详情
给你一个字符串 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)
时间内解决此问题的算法吗?
题解分析
解法一:双指针法
滑动窗口的思想:
用i
,j
表示滑动窗口的左边界和右边界,通过改变i
,j
来扩展和收缩滑动窗口,可以想象成一个窗口在字符串上游走,当这个窗口包含的元素满足条件,即包含字符串T的所有元素,记录下这个滑动窗口的长度j-i+1
,这些长度中的最小值就是要求的结果。
步骤一
不断增加j
使滑动窗口增大,直到窗口包含了T的所有元素
步骤二
不断增加i
使滑动窗口缩小,因为是要求最小字串,所以将不必要的元素排除在外,使长度减小,直到碰到一个必须包含的元素,这个时候不能再扔了,再扔就不满足条件了,记录此时滑动窗口的长度,并保存最小值
步骤三
让i
再增加一个位置,这个时候滑动窗口肯定不满足条件了,那么继续从步骤一开始执行,寻找新的满足条件的滑动窗口,如此反复,直到j
超出了字符串S范围。
如何判断滑动窗口包含了T的所有元素?
- 对于如何判断滑动窗口中包含所有元素,我们可以使用一个整数数组,类似于以前做过的字符串所有相同的字母异位词问题和字母异位词分组问题。
- 这里可以优化的一个地方就是,如果单纯使用cnts数组计数每个字符出现的次数,那么在判断的时候每次都需要判断数组中所有元素的数量都小于等于0时,表示当前滑动窗口不再需要任何元素。
- 具体地,我们可以再使用一个计数指针cnt表示目标串中总共需要计数的字符数,并在滑动窗口的过程中维护该变量。最后,只需要判断cnt是否为0即可。
class Solution {
public String minWindow(String s, String t) {
int n = t.length();
int cnt = n;// 记录目标串所有字符出现的总次数
int l=0, r=0;// 双指针的左右边界
int[] cnts = new int[128];// 记录某个英文字母(大小写字母最大ascii码为126)出现的次数
for(int i=0; i<n; i++){// 遍历t字符串,记录每个字符的出现次数
cnts[t.charAt(i)]++;
}
int minsize = Integer.MAX_VALUE;// 满足条件的最小窗口
String ans = "";
while(r < s.length()){// 遍历s字符串
char ch = s.charAt(r);
if(cnts[ch] > 0){// 需要该字符
cnt--;
}
cnts[ch]--;// 计数该字符,所需字符数量减一
if(cnt == 0){// 窗口中已经包含目标串中的所有字符
while(l < r && cnts[s.charAt(l)] < 0){// 不断向右移动左指针,但始终保持窗口中含有目标串的所有字符
cnts[s.charAt(l)]++;// 释放左指针所在字符
l++;
}
if(r - l + 1 < minsize){
minsize = r - l + 1;
ans = s.substring(l, r + 1);
}
// 左指针再向右移动一个位置,那么窗口中肯定不满足条件
cnts[s.charAt(l)]++;
l++;
cnt++;
}
r++;// 右指针右移
}
return ans;
}
}
结果展示
标签:子串,字符,cnt,窗口,++,76,字符串,滑动,LeetCode 来源: https://www.cnblogs.com/GarrettWale/p/15791585.html