题目
给你一个字符串 S、一个字符串 T 。请你设计一种算法,可以在 O(n) 的时间复杂度内,从字符串 S 里面找出:包含 T 所有字符的最小子串。
tag:Sliding window
diffculty:Hard
示例
输入:S = “ADOBECODEBANC”, T = “ABC”
输出:“BANC”
难点
- 如何确定这些元素都在win当中?
- 如何去缩减这些元素?
最开始的思路【弃】
使用一个hash表来存储这些旧元素的位置,然后如果这些元素都有位置了,表面当前window里面都存在这些元素,然后在移动的时候就维护这个hash表,找到他们之间最小的那个差。
最后这个思路我也就想想,其实我觉得这个思路不太清晰,于是还是去借鉴了人家大佬的思路,真的是又清晰又简单,完美的逻辑。
大佬的思路【√】
大佬的解答点这里
- 使用一个need数组来记录当前windows都有哪些字符,need的意思就是window里面需要得到的字符,如果不是必须的字符初始的值就是0
- 使用变量count来标记当前还有多少个元素没在win当中
- 当count为0的时候,将窗口做缩减操作,判断如果need中该元素需要小于0,就说明该元素不是必须的可以被移出window外部,直到遇到必须的元素这时就可以获得当前的window的局部最优解,在这个情况下就可以对当前结果做判断,如果小于历史的最小长度,就更新一下。
cpp版本的代码
class Solution {
public:
string minWindow(string s, string t) {
if (s.size() < t.size() || s.size() < 1 || t.size() < 1) return "";
int left = 0;
int right = 0;
//使用need数组来记录当前窗口所需要的元素的个数
//当滑动窗口内包含一个元素就将它的值减一,如果移出一个元素就加一
int start = 0 , size = INT_MAX;
vector<int> need(128,0);
//先初始化need数组,将目标放进去
for (int c : t) {
need[c] ++;
}
//再设置一个num用来记录还有几个元素没被满足
int count = t.size();
while (right < s.size()) {
int c = s[right];
if (need[c] > 0 ) {
count --;
}
need[c] -- ;
//窗口此时符合要求
if(count == 0) {
//left此时应该开始移动缩减窗口
while (left < right && need[s[left]] < 0) {
need[s[left]] ++;
left ++;
}
//更新一下size
if(size > right - left + 1) {
start = left;
size = right - left + 1;
}
//更新之后就要将左边界做右移操作(这个时候还是将第一个需要的字符移出。count需要++)
need[s[left]] ++;
left ++;
count ++;
}
right ++; //right 先增加
}
return size != INT_MAX ? s.substr(start ,size) : "";
}
};