[Leetcode] minimum window substring 最小字符窗口
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中寻找最小包含T的子串,不管字母顺序。
思路:采用桶排序。先将T中所有的元素放入hash表中,然后遍历字符串S。如果当前字符在hash表中没有,说明这个不在T中,则直接跳过。若在,则将hash表中对应的字符的个数减1,说明T中的字符已经找到一个了;此时,若对应字符的个数不小于0,说明这个字符是有效的。什么意思了?如:字符串S=“ABAC”找T=“AC”,第一次,找到“A”时,hash表中A对应的个数变为0,即为有效的A,计数器count记下了A的个数;再次遇到A时,说明T只有一个A,且已经被记下了,所以不需要再次计数。即,忽略重复的,找到T中全部的就行。那明明是第二个A更符合题意,为什么要记下第一个A,这个会在后面慢慢的说明。
这样直到T的中的每个字符都被记下了,且只记下一次。这时,我们更新最小的合乎题意的子字符串的长度。这时,我们遇到一个问题,就是,如果开始时,有很多字符不是T中的,但是我们记下的最小字符串包括这些,怎么办?我们只需不断的将左指针向右移动,直到遇到第一个在T中的字符,这个过程中,我们不断的更新minlen长度,这样我们就得到了这次的复合题意的最下子字符串的长度。
那么我们如何得到下一个符合题意的长度?此时,我们只需将最左端字符在hash表中的个数加1,然后count减1,就实现了,重新寻找下一个子字符串的循环。因为,会遍历完S,所以之前说的遇到第二个A时,会重新更新minlen,所以之前只用记住一个A的。参考了Grandyang的博客。代码如下:
1 class Solution { 2 public: 3 string minWindow(string S, string T) 4 { 5 if(T.size()>S.size()) return ""; 6 string res=""; 7 int left=0,count=0,minLen=S.size()+1; 8 unordered_map<char,int> m; 9 for(int i=0;i<T.size();++i) 10 { 11 if(m.find(T[i]) !=m.end()) 12 ++m[T[i]]; 13 else 14 m[T[i]]=1; 15 } 16 17 for(int right=0;right<S.size();++right) 18 { 19 if(m.find(S[right]) !=m.end()) 20 { 21 --m[S[right]]; 22 if(m[S[right]]>=0) 23 ++count; 24 while(count==T.size()) 25 { 26 if(right-left+1<minLen) 27 { 28 minLen=right-left+1; 29 res=S.substr(left,minLen); 30 } 31 if(m.find(S[left]) !=m.end()) 32 { 33 ++m[S[left]]; 34 if(m[S[left]]>0) 35 --count; 36 } 37 left++; 38 } 39 } 40 } 41 return res; 42 } 43 };
解题思路:其实就是通过双指针维持一个Window,窗口右指针向右扩张用来找到包含子串为目的,窗口左指针向右收缩以使子串最小。典型的滑动窗口方法的实现。
也可以用数组来替代hash表,参考 曲高和寡_健的博客,代码如下:
1 class Solution { 2 public: 3 string minWindow(string S, string T) 4 { 5 int sLen=S.size(),tLen=T.size(); 6 if(sLen==0||tLen==0) 7 return ""; 8 vector<int> sHash(256,0),tHash(256,0); 9 for(int i=0,i<tLen;++i) 10 { 11 tHash[T[i]]++; 12 } 13 14 int beg=-1,end=sLen,count=0,minLen=sLen; 15 for(int i=0,start=i;i<sLen;++i) 16 { 17 ++sHash[S[i]]; 18 if(sHash[S[i]]<=tHash[S[i]]) 19 ++count; 20 21 if(count==tLen) 22 { 23 while(start<i&&sHash[S[start]]>tHash[S[start]]) 24 { 25 --sHash[S[start]]; 26 start++; 27 } 28 29 if(i-start<minLen) 30 { 31 minLen=i-start; 32 beg=start; 33 end=i; 34 } 35 36 --sHash[S[start++]]; //寻找下一个符合要求的子字符串 37 --count; 38 } 39 } 40 if(beg==-1) 41 return ""; 42 return S.substr(beg,end-beg+1); 43 44 } 45 };
这种方法看的更为清晰些。