思路:
题目的主要信息:
- 在S字符串中找到包含T字符串所有字符的最小字串
- 要求时间复杂度
- 如果S中没有包含T中所有字符的子串,返回空字符串"",若有有则存在唯一最短
方法一:滑动窗口+哈希表+双指针
具体做法:
- 维护一个哈希表,里面是字符串T的字符为key值,初始时当字符在T中出现一次则减一。
- 依次遍历字符串S,如果匹配则将哈希表中的内容加一。
- 在遍历过程维护一个窗口,如果哈希表中所有元素都大于0,则窗口收缩向左移动,如果不满足这个条件则窗口右移继续匹配。
- 窗口移动的时候需要更新最小窗口,以取得最短。
- 如果匹配到最后,窗口left(初始为-1)也没有右移,说明没有找到,返回空串。
- 最后截取S的窗口返回即可。
class Solution { public: bool check(unordered_map<char, int> &hash) { //煎炒是否有小于0的 for (auto iter = hash.begin(); iter != hash.end(); iter++) { if (iter->second < 0) return false; } return true; } string minWindow(string S, string T) { int cnt = S.length() + 1; unordered_map<char, int> hash; //记录目标字符串T的字符个数 for(int i = 0; i < T.length(); i++) hash[T[i]] -= 1; //初始化哈希表都为负数,找的时候再加为正 int slow = 0, fast = 0; int left = -1, right = -1; //记录左右区间 for (; fast < S.length(); fast++) { char c = S[fast]; if (hash.count(c)) //目标字符匹配+1 hash[c]++; while (check(hash)) { //没有小于0的说明都覆盖了,缩小窗口 if (cnt > fast - slow + 1) { //取最优解 cnt = fast - slow + 1; left = slow; right = fast; } char c = S[slow]; if (hash.count(c)) hash[c]--; //缩小窗口的时候减1 slow++; //窗口缩小 } } if (left == -1) //找不到的情况 return ""; return string(S.begin() + left, S.begin() + (right + 1)); } };
复杂度分析:
- 时间复杂度:,其中C为T字符串的字符集大小,为字符串S的长度,为字符串T的长度
- 空间复杂度:,哈希表长度不会超过字符串T的字符集大小
方法二:滑动窗口+双哈希表
具体做法:
建立两个哈希表,hashS记录滑动窗口中各个字符串出现的次数,hashT记录字符串T中各个字符出现的次数。如果hashS包含所有的hashT的字符,并且所有字符次数都不小于hashT,则说明当前窗口能够完全匹配,我们需要找的是其中的最小值。
我们可以遍历S字符串,每次窗口向右移动一步,如果加入了一个新的S[i],hashS中S[i]的次数还是小于hashT,则当前S[i]是必须的,我们准备一个cnt变量,并加一,用来维护S字符串滑动窗口内满足T字符串的元素的个数。一旦出现左边窗口的字符在HashS中大于hashT,我们将左边窗口往右移动,并使原来的计数减一,因为hashS记录的是滑动窗口中出现的数量,收缩窗口后它就不在滑动窗口了。当cnt=T.length()
,即找到了完全匹配,可以更新答案,因为要找到最短窗口,所以还需要继续往右直到遍历结束。
class Solution { public: string minWindow(string S, string T) { unordered_map<char, int> hashS; unordered_map<char, int> hashT;//记录字符串出现的次数 string res; int cnt = 0;//当cnt=T.length找到 for(int i = 0; i < T.length(); i++) hashT[T[i]]++; for(int i = 0, j = 0; i < S.length(); i++) { //遍历S,i为当前下标,包含窗口起点 hashS[S[i]]++; if(hashS[S[i]] <= hashT[S[i]]) //找到一个包含字符 cnt++ ; while (hashS[S[j]] > hashT[S[j]]) hashS[S[j++]]--; if (cnt == T.length()) { //找到包含,截取字串 if (res.empty() || i - j + 1 < res.length()) res = S.substr(j, i - j + 1); } } return res; } };
复杂度分析:
- 时间复杂度:,初始化哈希表遍历T字符串,后续又遍历S字符串
- 空间复杂度:,哈希表长度不会超过字符串T和S的字符集大小