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.
分析:暴力解决可以在O(n^2)时间复杂度内完成:遍历S,当碰到T中的首字母时,以该字母为窗口起始位置找到一个最小窗口包含所有T中的字符,最后取所有窗口的最小值。在暴力方法中我们做了很多重复的事情,比如S = “aabc”,T = “abc”,当我们以第一个a为窗口起始位置遍历时,我们要判断b、c是否在T中,当我们以第二个a为窗口起始位置遍历时,我们还要判断b、c是否在T中。那么怎样避免这种重复的工作呢?
可以利用两个指针扫描(两个指针分别为start,i),以S = “e b a d b a c c b”(忽略空格),T = “abc”为例:
0 1 2 3 4 5 6 7 8
- 初始化 start = i = 0
- i 逐渐往后扫描S直到窗口S[start…i]包含所有T的字符,此时i = 6(字符c的位置)
- 缩减窗口:此时我们注意到窗口并不是最小的,需要调整 start 来缩减窗口。缩减规则为:如果S[start]不在T中 或者 S[start]在T中但是删除后窗口依然可以包含T中的所有字符,那么start = start+1, 直到不满足上述两个缩减规则。缩减后i即为窗口的起始位置,此例中从e开始窗口中要依次删掉e、b、a、d,start最后等于4 ,那么得到一个窗口大小为6-4+1 = 3
- start = start+1(此时窗口肯定不会包含所有的T中的字符),跳转到步骤2继续寻找下一个窗口。本例中还以找到一个窗口start = 5,i = 8,比上个窗口大,因此最终的最小窗口是S[4…6]
具体实现时,要用哈希表来映射T中字符以便在O(1)时间内判断一个字符是否在T中,由于是字符缘故,可以用数组简单的来实现;还需要一个哈希表(map<char,int>)来记录扫描时T中的某个字符在S中出现的次数,也可以用数组实现
C++代码实现:
#include<iostream> #include<string> using namespace std; class Solution { public: string minWindow(string S, string T) { int lens=S.length(); int lent=T.length(); int tcnt[256]={0}; int fcnt[256]={0}; int i,start; int found=0; int winStart=-1,winEnd=lens; for(i=0;i<lent;i++) tcnt[T[i]]++; for(i=0,start=0;i<lens;i++) { if(tcnt[S[i]]!=0) { fcnt[S[i]]++; if(fcnt[S[i]]<=tcnt[S[i]]) found++; if(found==lent) { //找到一个窗口之后调整start的位置 while(tcnt[S[start]]==0||fcnt[S[start]]>tcnt[S[start]]) { if(tcnt[S[start]]) fcnt[S[start]]--; start++; } if(i-start<winEnd-winStart) { winStart=start; winEnd=i; } fcnt[S[start]]--; start++; found--; } } } return winStart==-1?"":S.substr(winStart,winEnd-winStart+1); } }; int main() { Solution s; string S="SADOBECODEBANC"; string T="ABC"; cout<<s.minWindow(S,T)<<endl; }