给定一个字符串 S 和一个字符串 T,请在 S 中找出包含 T 所有字母的最小子串。
示例:
输入: S = "ADOBECODEBANC", T = "ABC" 输出: "BANC"
说明:
- 如果 S 中不存这样的子串,则返回空字符串
""
。 - 如果 S 中存在这样的子串,我们保证它是唯一的答案。
思路:这个题目的思路是维护两个指针l 和 r(初值都为0),分别标识覆盖子串的开始位置和结束位置。采用哈希表bb记录每个字符数量,用一个for循环O(n)的时间复杂度来解决。接下来存在三个问题:1、什么时候更新左指针; 2、什么时候更新右指针; 3、什么时候更新最小覆盖子串
1、每次找到一个新的覆盖子串的时候,l就更新为下一个最近的t中存在的字符的位置。
2、每次找到一个新的覆盖子串的时候,r就更新为i。
3、当前覆盖子串比之前的短的时候。
那么什么时候算找到了一个新的覆盖字串儿呢?注意,t里面的字符可能是有重复的,所以说bb.size()== aa.size() 的时候不足以说明找到了一个新的覆盖子串,必须得是bb中的每一个字符的数目都大于等于aa中的每一个字符的数量,这一点我们通过哈希表来维护。
class Solution { public: string minWindow(string s, string t) { string re; map<char,int> aa, bb; for (auto x : t) ++aa[x]; int l = 0, r = 0; for (int i = 0; i < s.size(); ++i){ if (aa.count(s[i])){ if (bb.empty()) l = i;//l的边界 ++bb[s[i]];//维护哈希表 while (bb.size()== aa.size()){//这里为什么用while?因为找到新的覆盖字串,更新l之后,这里面可能还有覆盖字串。 int flag = 1; for (auto it = aa.begin(); it != aa.end(); ++it){ if ((*it).second > bb[(*it).first]) flag = 0; } if (flag) {//flag为1的时候说明找到了新的覆盖子串 r = i;//更新r if (re.empty() || r - l + 1<re.size())//更新re re = s.substr(l, r - l + 1); if (--bb[s[l]] == 0) bb.erase(s[l]);//因为要移动l,维护下哈希表 while (l<s.size() - 1 && !aa.count(s[++l]));//更新l,注意l不要越界 } else break; } } } return re; } };