- 滑动窗口
使用两个哈希表,hs 哈希表维护的是 s 字符串中滑动窗口中各个字符出现多少次,ht 哈希表维护的是 t 字符串各个字符出现多少次。如果 hs 哈希表中包含 ht 哈希表中的所有字符,并且对应的个数都不小于 ht 哈希表中各个字符的个数,那么说明当前的窗口是可行的,可行中的长度最短的滑动窗口就是答案。
- 遍历t字符串,用 ht 哈希表记录t字符串各个字符出现的次数。
- 定义两个指针 j 和 i,j 指针用于收缩窗口,i 指针用于延伸窗口,则区间 [j, i] 表示当前滑动窗口。首先让 i 和 j 指针都指向字符串 s 开头,然后枚举整个字符串 s ,枚举过程中,不断增加i使滑动窗口增大,相当于向右扩展滑动窗口。
- 每次向右扩展滑动窗口一步,将 s[i] 加入滑动窗口中,而新加入了 s[i],相当于滑动窗口维护的字符数 +1,即 ++hs[s[i]]。
- 对于新加入的字符 s[i], 如果 hs[s[i]] <= ht[s[i]],说明当前新加入的字符 s[i] 是必需的,且还未到达字符串 t 所要求的数量。我们还需要事先定义一个 cnt 变量, cnt 维护的是 s 字符串 [j, i] 区间中满足 t 字符串的元素的个数,记录相对应字符的总数。新加入的字符**s[i]**必需,则 ++cnt。
- 我们向右扩展滑动窗口的同时也不能忘记收缩滑动窗口。因此当 hs[s[j]] > ht[s[j] 时,说明 hs 哈希表中 s[j] 的数量多于 ht 哈希表中 s[j] 的数量,此时我们就需要向右收缩滑动窗口,++j 并使 –hs[s[j]]。
- 当 cnt == t.size() 时,说明此时滑动窗口包含符串 t 的全部字符。我们重复上述过程找到最小窗口即为答案。
class Solution
{
public:
string minWindow(string s, string t)
{
unordered_map<char, int> hs, ht;
for (auto temp : t) //存放t
{
++ht[temp];
}
int cnt = 0;//计算hs中加入的字符数
string res = "";
for (int i = 0, j = 0; i < s.size(); ++i)
{
++hs[s[i]];
if (hs[s[i]] <= ht[s[i]])//滑动窗口右边界右移
{
++cnt;
}
while (hs[s[j]] > ht[s[j]])//滑动窗口左边界左移
{
--hs[s[j]];
++j;
}
if (cnt == t.size())
{
if (res.empty() || i - j + 1 < res.size())//res.empty()说明第一次出现cnt == t.size(),当后面再次出现cnt == t.size()时,i-j+1一定要小于res.size()
{
res = s.substr(j, i - j + 1);
}
}
}
return res;
}
};
部分参考自:
https://leetcode-cn.com/problems/minimum-window-substring/solution/leetcode-76-zui-xiao-fu-gai-zi-chuan-cja-lmqz/