Given a string S and a string T, find the minimum window in S which will contain all the characters in T in complexity O(n).
For example,
S = "ADOBECODEBANC"
T = "ABC"
Minimum window is "BANC"
.
Note:
If there is no such window in S that covers all characters in T, return the emtpy string ""
.
If there are multiple such windows, you are guaranteed that there will always be only one unique minimum window in S.
我们以下使用 S = "acbbaca" and T = "aba" 来演示这个算法。
基本思路是在遍历S的过程中。使用两个指针(合法window的begin和end索引)和两个table(needToFind和hasFound),needToFind保存T中每一个字母的个数(二娃:相当于我们的needToFill)。hasFound保存当前收集到的每一个字母的个数。我们也同一时候使用一个count变量。用来保存当前收集到的字母总数。可是那些收集过了的字母数并不算在count里面。这种话,当count等于T.length,那我们就知道遇到一个合法的window了。
我们利用end指针来遍历S,假设当前end指向S中的字母x,假设x是T中的字母,hasFound[x]加一。假设hasFound[x]眼下还小于等于needToFind[x](二娃:说明字母x还没有收集全或者刚刚收集全哦)。那么我们同一时候也添加count。当合法window的条件满足,也就是count等于T.length,我们马上递增begin指针。而且保证在递增的过程中始终有count等于T.length。
在递增begin指针的过程中,我们怎么样才干保证“始终有count等于T.length”呢?
如果begin指向字母x,如果hasFound[x]大于了needToFind[x]。hasFound[x]减去一。同一时候递增begin。(二娃:这里非常有画面感哦。
由于当前遇到的x是冗余的靠左的字母,这里的操作事实上等价于前面两个算法中的“删除charAppearanceRecorder中对应的字母的链表头节点”,有点儿像一个是lazy去重,一个是eager去重)否则的话,当前的begin就是window的起始索引了。
接下来我们就能够通过end - begin + 1得到当前window的长度了。
这里便能够更新最小window长度。
算法实际上首先找到第一个合法的window,然后在接下来的扫描过程中保持window的合法性(二娃:事实上就是count 始终小于等于(当遇到新window)T.length)。
看以下的图图。
i)S = "acbbaca" and T = "aba".
ii)找到第一个合法的window。
这里注意我们不能递增begin指针由于hasFound['a'] 等于 needToFind['a'],即2. 假设我们此时递增begin。那就不是合法window了。
iii)找到第二个合法的window。
begin指针指向第一个a。hasFound['a']等于3,而needToFind['a']等于2,说明这个a是一个冗余的a。我们递减hasFound['a']同一时候递增begin。
iv)我们也须要跳过那些不在T中的字母,比如上面的c。
如今beging指向了b,hasFound['b']等于2,大于了needToFind['b'],说明这也是一个冗余的b,我们递减hasFound['a']同一时候递增begin。
v)begin指向b,这时候hasFound['b']等于needToFind['b']。不能再减了。同一时候begin也不能再次移动了,这里就是一个短window的起点位置。
begin和end都最多前进N次。从而整个算法运行小于2N. 复杂度是O(N)。
class Solution { private: int count1[256]; int count2[256]; public: string minWindow(string S, string T) { // Start typing your C/C++ solution below // DO NOT write int main() function if (T.size() == 0 || S.size() == 0) return ""; memset(count1, 0, sizeof(count1)); memset(count2, 0, sizeof(count2)); for(int i = 0; i < T.size(); i++) { count1[T[i]]++; count2[T[i]]++; } int count = T.size(); int start = 0; int minSize = INT_MAX; int minStart; for(int end = 0; end < S.size(); end++) { if (count2[S[end]] > 0) { count1[S[end]]--; if (count1[S[end]] >= 0) count--; } if (count == 0) { while(true) { if (count2[S[start]] > 0) { if (count1[S[start]] < 0) count1[S[start]]++; else break; } start++; } if (minSize > end - start + 1) { minSize = end - start + 1; minStart = start; } } } if (minSize == INT_MAX) return ""; string ret(S, minStart, minSize); return ret; } };