一、题目

二、 示例

三、提示

四、 思路与代码实现
1. 思路
- 本题, 套用的是滑动窗口算法模板;
- 初始化左右窗口边界指针(要方便源串取值) left = 0, right = 0, 为什么这样初始化?
- 若设置窗口索引为
左闭右闭
区间, 则这样初始化时,窗口为windows[0, 0], 即初始化时就包含了一个元素; - 若设置窗口索引为
左开右开
区间, 则这样初始化时,窗口为windows(0, 0), 那么当right像右滑动的一格时候, 窗口windows(0, 1)仍然没有元素, 此时right 指针要多滑动一位才有包含; - 以上两种都不方便处理边界;
- 而
左闭右开
区间,当初始化时, 窗口为windows[0, 0), 此时窗口中包含一个元素windows[0],方便处理边界;
- 使用
unordered_map<char, int>
容器来定义windows(窗口)与need, 其中 need 记录 T中字符出现的次数, 而windows则记录窗口中字符的出现次数; - 接着
先滑动 right 指针来扩大窗口
, 其中若窗口中的字符与need中的字符匹配(数量上也要匹配),则val++(val代表窗口中满足 need 条件的字符个数 例如, T 为ABC
, 而滑动窗口包含为 B
NB
AC); - 而
收缩窗口的时机
为, 当 val == need.size()
即窗口中包含的字符满足了need条件, 此时窗口已经完全覆盖了子串 T;在这里做子串的更新, 若 right - left 这一范围比之前的 len 长度更小则长度len更新, 且让 start = left; - 返回子串为:
return len == INT_MAX_LEN ? "" : s.substr(start, len);
2. 代码
class Solution {
public:
string minWindow(string s, string t) {
const int INT_MAX_LEN = 1e6;
unordered_map<char, int> windows, need;
for (char c : t) need[c]++;
int left = 0;
int right = 0;
int val = 0;
int start = 0;
int s_len = s.size();
int len = INT_MAX_LEN;
while (right < s_len) {
char c = s[right];
right++;
if (need.count(c)) {
windows[c]++;
if (windows[c] == need[c]) {
val++;
}
}
while (val == need.size()) {
if (right - left < len) {
start = left;
len = right - left;
}
char d = s[left];
left++;
if (need.count(d)) {
if (windows[d] == need[d]) {
val--;
}
windows[d]--;
}
}
}
return len == INT_MAX_LEN ? "" : s.substr(start, len);
}
};
3. 关键思考
- 滑动窗口算法的关键:
- 1、什么时候应该扩大窗口?
- 2、什么时候应该缩小窗口?
- 3、什么时候应该更新答案?