给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 “” 。
本题的思想是哈希+滑动窗口,使用哈希构建sStr(s子串中字符出现次数)和tStr(t字符串中字符出现次数),如果sStr能够覆盖tStr,则当前子串符合条件,记录当前子串左端位置和长度,以便最后分割子串,并将子串左端位置右移一位;如果不能覆盖,则将子串右端位置右移一位。
代码如下:
class Solution {
public:
//定义两个哈希表,tstr用来存放t中元素的出现次数信息,sstr用来存放滑动窗口中元素的出现次数信息
unordered_map<char,int> tstr,sstr;
//检查当前的窗口是否是合格的窗口,即:
//检查当前滑动窗口中元素是否完全覆盖了字符串t中的所有元素(重点是某元素的出现次数必须不小于t中对应元素的出现次数)
bool check()
{
for(auto tchar : tstr)
{
if(tchar.second > sstr[tchar.first]) return false;//注意这里的判断条件是大于
//只要sstr中元素的second值不小于tchar中对应元素的second值就行
}
return true;
}
string minWindow(string s, string t) {
int n1 = s.size(),n2 = t.size();
if(n1<n2) return "";//如果t串比s串还长,s中肯定不能找到相应的子串
int len = INT_MAX;//最小窗口的长度
int ans_left = -1;//最小窗口的左边界指针
//构造哈希表
for(auto tchar : t)
++tstr[tchar];
int left = 0,right = 0;//窗口的左右两端指针
//滑动窗口右端指针遍历整个s串
for(int right = 0;right<n1;right++)
{
//每遍历一个元素,更新sstr中的元素信息
++sstr[s[right]];
//如果当前遍历到的元素在tstr中也有,说明此次遍历的更新是有效的更新,否则不用管,直接继续遍历
if(tstr.find(s[right]) != tstr.end())
{
//对于每一次有效的更新,检查当前窗口中的子串是否是一个合格的子串
while(check() && left<=right)
{
//如果当前子串是合格的,那么判断是否是最小的窗口
if(len > right - left +1)
{
//如果是最小的窗口,那么更新窗口信息
ans_left = left;
len = right - left + 1;
}
//当前子串如果是合格的,那么尝试移进窗口的左边界缩短窗口的长度
--sstr[s[left]];//窗口左边界的元素信息从哈希表sstr中删除
left++;//移进窗口左边界
//移进之后,继续while判断移进后的子串是否是合格的,如果是合格的,继续重复同样的操作,更新窗口的信息
}
//一旦窗口不合格,窗口右边界的指针就继续往后遍历,拓宽窗口的长度
}
}
if(ans_left == -1) return "";
else return s.substr(ans_left,len);
}
};