题目如下
给定两个字符串 s 和 t 。返回 s 中包含 t 的所有字符的最短子字符串。如果 s 中不存在符合条件的子字符串,则返回空字符串 “” 。
如果 s 中存在多个符合条件的子字符串,返回任意一个。
注意: 对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。
示例 1:
输入:s = “ADOBECODEBANC”, t = “ABC”
输出:“BANC”
解释:最短子字符串 “BANC” 包含了字符串 t 的所有字符 ‘A’、‘B’、‘C’
示例 2:
输入:s = “a”, t = “a”
输出:“a”
示例 3:
输入:s = “a”, t = “aa”
输出:""
解释:t 中两个字符 ‘a’ 均应包含在 s 的子串中,因此没有符合条件的子字符串,返回空字符串。
进阶:你能设计一个在 o(n) 时间内解决此问题的算法吗?
解题代码和注释
class Solution
{
public:
string minWindow(string s, string t)
{
unordered_map<char, int> hs, ht;
string ans;
for (auto& c : t)
{
ht[c]++;
}
int l = 0, r = 0, cnt = 0; //l为窗口的左边界,r为窗口的右边界
for (; r < s.size(); r++)
{ //每次循环右边界右移一次
hs[s[r]]++;
if (hs[s[r]] <= ht[s[r]])
{
cnt++;//在找到第一个符合条件的窗口后,这个语句不会再运行了。
} //ps.该语句的作用是统计窗口内的有效字符
//左边界右移
while (hs[s[l]] > ht[s[l]])
{ //这个语句会右移左边界,比如这个边界字符不在t里,
hs[s[l]]--; //或者符合条件的边界值在后面又增加了一个且和左边界值相同,那么就可以右移左边界
l++;
}
if (cnt == t.size())
{ //有效字符等于字符串t的长度,我们可以放入答案;或者对比前窗口的大小和当前的大小,然后决定是否更新ans。
if (ans.empty() || r - l + 1 < ans.size())
{
ans = s.substr(l, r - l + 1);
}
}
}
return ans;
}
};