给你一个字符串 S、一个字符串 T,请在字符串 S 里面找出:包含 T 所有字母的最小子串。
示例:
输入: S = "ADOBECODEBANC", T = "ABC"
输出: "BANC"
说明:
- 如果 S 中不存这样的子串,则返回空字符串 “”。
- 如果 S 中存在这样的子串,我们保证它是唯一的答案。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/minimum-window-substring
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
完整代码
参考:滑动窗口
基本思想
滑动窗口:
ps:刚开始想到了用滑动窗口的思想,但是不知道该如何更新两个指针,下面重点说明该如何更新两个指针
- 初始时,用两个指针来指示滑动窗口,滑动窗口的大小为0,两个指针都指向字符串s的起始位置
- 循环条件:右指针移动到字符串s的尾部时,循环结束
- 如何更新右指针:当窗口内不存在题目要求的子串t时,更新右指针,增大窗口
- 如何更新左指针:当窗口内存在题目要求的子串时,更新左指针,缩小窗口
该题目还有十分关键的一点:如何判断窗口内的字符串是否已经覆盖了题目要求的子串?
- 刚开始时,定义了两个map,每次都统计窗口内每个字符的个数,然后再和待覆盖子串进行对比,这种思路能够通过大部分测试用例,剩下最后两个由于超出时间限制无法ac.
- 另一种思路:也是参考了一部分别人的思想进行优化
对于窗口子串,在右指针移动的同时,统计字符出现的次数,左指针移动时,递减次数,这个好处理。
那如何判断是否覆盖呢?
依然是在左右指针移动的过程中进行统计,右指针移动时,如果当前字符在待覆盖子串中,且恰好和待覆盖子串中的数目相等,将匹配的个数+1(注意:这里只有相等时才+1,如果出现大于的情况,其实已经在相等的时候统计了,避免重复统计);左指针移动时,如果递减后恰好使得数目不满足要求,则-1
class Solution {
public:
string minWindow(string s, string t) {
string res = "";
if(s.size() < t.size())
return res;
int left = 0, right = 0;
int res_s = s.size(), res_e = INT_MAX;
//将res_s初始化成s.size()是因为当不包含子串的时候,截取的结果串为空
//将res_e初始化成最大值的目的是:遇到第一个结果串时能正确保存结果
//将字符串T映射成map
map<char,int> m, cur;
int i, match = 0;//match记录s中有几个字符已经符合t的要求了
for(int i = 0; i < t.size(); ++i){
++m[t[i]];
}
//统计第一个字符
++cur[s[right]];
if(m.count(s[right]) && cur[s[right]] == m[s[right]])
++match;
while(right < s.size()){
//cur.clear();
//if(right - left >= t.size() - 1){
// for(i = left; i <= right; ++i){
// ++cur[s[i]];
// }
// for(i = 0; i < t.size(); ++i){
// if(cur[t[i]] < m[t[i]])
// break;
// }
if(match == m.size()){//包含t中的所有字母
if(right - left < res_e - res_s){//新窗口比原窗口小
res_s = left;
res_e = right;
}
if(m.count(s[left]) && cur[s[left]] == m[s[left]])
--match;
--cur[s[left]];
++left;//缩小窗口
}
// else{//不包含
// ++right;//向右扩张
// ++cur[s[right]];
// if(m.count(s[right]) && cur[s[right]] >= m[s[right]])
// ++match;
// }
//}
else{//窗口太小向右扩张
++right;
++cur[s[right]];
if(m.count(s[right]) && cur[s[right]] == m[s[right]])
++match;
}
}
res = s.substr(res_s, res_e - res_s + 1);
return res;
}
};
另一种代码写法
class Solution {
public:
string minWindow(string s, string t) {
//滑动窗口
int left = 0, right = 0, match = 0;
int st = 0, minlen = INT_MAX;
map<char, int> m, cur;
string res;
//1.统计t中的字符数
for(char c : t)
++m[c];
//2.寻找最小覆盖子串
while(right < s.size()){
char c = s[right];
if(m.count(c)){//只统计滑动窗口中在s中的字符
++cur[c];
if(cur[c] == m[c])
++match;
}
//++right;
while(match == m.size()){
char c1 = s[left];
if(right - left + 1 < minlen){
st = left;
minlen = right - left + 1;
}
if(m.count(c1)){
if(cur[c1] == m[c1])
--match;
--cur[c1];
}
++left;
}
++right;
}
res = (minlen == INT_MAX)? "" : s.substr(st, minlen);
return res;
}
};