题目叙述
给你一个字符串 S、一个字符串 T 。请你设计一种算法,可以在 O(n) 的时间复杂度内,从字符串 S 里面找出:包含 T 所有字符的最小子串。
示例:
输入:S = “ADOBECODEBANC”, T = “ABC”
输出:“BANC”
提示:
如果 S 中不存这样的子串,则返回空字符串 “”。
如果 S 中存在这样的子串,我们保证它是唯一的答案。
分析
本题属于典型的滑动窗口的题目,滑动窗口的核心在于:
- 遍历求解连续的子字符串的问题
- 对时间复杂度有一定的高效率
- 设定左指针和右指针
- 什么时候更新左指针什么时候更新右指针,本处主要当满足条件的时候,不断收缩窗口,即不断右移动左指针,并且有可能一直符合条件,所以要把该条件判断放在一个while循环中。当不满足条件的时候就要正常地右移动指针。
滑动窗口的步骤如下:
- 确定左右指针的初始值(左指针的意义是当前的窗口的左边界,右指针的意义就是当前的窗口的右边界,因此当前的窗口的长度为r-l+1)。
- 一般是二层循环,但是时间复杂度一般都是O(n)。
代码的模板为:
string minWindow(string s, string t) {
int l = ;
int r = ;
while(r < int(s.size())){
//处理逻辑
r++;
while(check()){
...
++l;
}
}
return ;
}
代码
注意此处的 while(check())前面的逻辑只能放在上面,否则以s.size()-1结尾的字符串就无法被处理。
class Solution {
public:
unordered_map <char, int> ori, cnt;
bool check() {
for (const auto &p: ori) {
if (cnt[p.first] < p.second) {
return false;
}
}
return true;
}
string minWindow(string s, string t) {
string ans = "";
for(const auto &c: t){
++ori[c];
}
int l = 0, r = -1;
int len = INT_MAX;
while(r < int(s.size())){
// if(ori.find(s[++r]) != ori.end()){
// ++cnt[s[++r]];
// }
++cnt[s[++r]];//此处要先进行++,不能使用s[r++],因为我们指定r为当前的字符串的结尾的而不是子字符串的下一个位置,由于要先++,所以r的初始值要初始化为-1而不是0
while(check()){
if(r - l + 1 < len){
len = r - l + 1;
ans = s.substr(l, len);
}
// if(ori.find(s[l]) != ori.end()){//ori对应的是t, 如果当前的左指针的位置元素在ori中,其实也可以不用判断
--cnt[s[l]];
++l;
}
}
return ans;
}
};