给你一个字符串 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 的子串中,
因此没有符合条件的子字符串,返回空字符串。
使用滑动窗口方法(变长的滑动窗口)
采用了labuladongd的方法:
https://labuladong.gitee.io/algo/1/12/
class Solution
{
public:
string minWindow(string s, string t)
{
unordered_map<char, int> need, window;//哈希表need和window,以键值方式存储每个字符出现的次数
for (char c : t) //初始化need
need[c]++;
int left = 0, right = 0;
int valid = 0;
int start = 0, len = INT_MAX; //记录最小覆盖子串的起始位置及长度
while (right < s.size()) //判断右侧窗口是否需要扩展
{
char c = s[right];
right++;
if (need.count(c))
{
window[c]++;
if (window[c] == need[c])
valid++;
}
while (valid == need.size()) //判断左侧窗口是否需要收缩
{
if (right - left < len) //更新最小覆盖字串
{
start = left;
len = right - left;
}
char d = s[left];
left++;
if (need.count(d))
{
if (window[d] == need[d])
valid--;
window[d]--;
}
}
}
return len == INT_MAX ?"" : s.substr(start, len);//返回最小覆盖子串
}
};
思路:
1、初始化 left=right=0
,把索引左闭右开区间 [left, right)
称为一个「窗口」。
2、我们先不断地增加 right
指针扩大窗口 [left, right)
,直到窗口中的字符串符合要求(包含了 T
中的所有字符)。
3、此时,我们停止增加 right
,转而不断增加 left
指针缩小窗口 [left, right)
,直到窗口中的字符串不再符合要求(不包含 T
中的所有字符了)。同时,每次增加 left
,我们都要更新一轮结果。
4、重复第 2 和第 3 步,直到 right
到达字符串 S
的尽头。
滑动窗口题目需要考虑三点:
1、什么时候应该扩大窗口?
2、什么时候应该缩小窗口?
3、什么时候得到一个合法的答案?