leetcode--Minimum Window Substring

1.题目描述

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.

2.解法分析

最开始理解错了题目的意思,以为T需要先找出不重复的字符,即T=”ABC”等效于T=”AABC”,后来提交的时候才发现我想复杂了,但是这个代码写出来了也不能浪费,代码思路如下:先从尾部开始找出包含所有字符的最短的字符串作为初始化步骤,确定初始的子串首尾,然后不断将子串的尾部往前移,这过程中每一步子串的首部可能不变,也可能前移,在这过程中记录当前的最短子串长度及其首尾信息,直至不能前移为止。代码如下。

class Solution {
public:
    string minWindow(string S, string T) {
        // Start typing your C/C++ solution below
        // DO NOT write int main() function
        map<char,int> counter;
        unordered_set<char>us_bk;
        unordered_set<char>us;
        
        int minLen;
        int minStart=0;
        int minEnd=S.length()-1;
        int nextStart=S.length()-1;
        if(S.empty()||T.empty())return S;
        if(S.length()<T.length())return "";
        
        int i=0;
        while(i<T.length())
        {
            us_bk.insert(T[i]);i++;
        }
        
        unordered_set<char>::iterator iter;
        for(iter=us_bk.begin();iter!=us_bk.end();++iter)
        {
            counter.insert(make_pair(*iter,0));
        }
        
        i=S.length()-1;
        while(i>=0)
        {
            if(us_bk.count(S[i])==1)
            {
                if(us.empty())minEnd=i;
                                  
                us.insert(S[i]); counter[S[i]]+=1;
                
                if(us.size()==us_bk.size())
                {
                    minStart=i;
                    minLen=minEnd-minStart+1;break;
                }
            }
            i--;
        }
        
        if(us.size()!=us_bk.size())return "";
        
        int nextLen=minLen;
        int nextMinStart=minStart;
        int nextMinEnd=minEnd;
        while(true)
        {
            int j=nextMinEnd-1;
            if(j<0)break;
            while(j>=0&&us_bk.count(S[j]==0))j--;
            if(j<0)break;
            if(counter[S[nextMinEnd]]==1)
            {
                int k=nextMinStart-1;
                while(k>=0)
                {
                    if(us_bk.count(S[k]))
                    {
                        if(S[k]==S[nextMinEnd])
                        {
                            nextMinStart=k;
                            break;                        
                        }
                        else counter[S[k]]+=1;
                    }
                    k--;
                }
                
                if(k<0)break;
                
                nextMinEnd=j;
                nextLen=nextMinEnd-nextMinStart+1;
                if(nextLen<minLen)
                {
                    minStart=nextMinStart;minEnd=nextMinEnd;minLen=nextLen;
                }
                
            }
            
            else
            {
                counter[S[nextMinEnd]]-=1;
                nextMinStart = minStart;
                nextMinEnd=j;
                nextLen=nextMinEnd-nextMinStart+1;  
                if(nextLen<minLen)
                {
                    minStart=nextMinStart;
                    minEnd=nextMinEnd;
                    minLen=minEnd-minStart+1;
                }
            }            
        }
        
        return S.substr(minStart,minLen);
        
    }
};

老实说,这个代码有点长。⊙﹏⊙b汗

image

如果按照提议了解就需要重写一下代码。

稍微改动就可以获得一个可以AC的代码。

class Solution {
public:
    string minWindow(string S, string T) {
        // Start typing your C/C++ solution below
        // DO NOT write int main() function
        //areslipan@163.com
        /*算法思路:
            1.从尾部第一个属于T中的字符开始寻找最短的包含这个所有在T中出现过的字符的的子串,信息存入counter中
                设最终求出的字符串首部微minStart,结束点为minEnd,那么用这个子串的信息初始化minStart和minEnd
            2.删除子串的最后一个字符t(这个字符一定属于T),然后往前删除不属于T的字符,直至找到第二个属于T的字符,如果找不到,退出,
             如果找得到,将值赋给nextMinEnd,由于删除了字符t,如果删除t之后的字符串还是符合要求的,那么当前子串的信息可以更新。即nextMinEnd可以前移。
             如果不满足,那么要从nextMinStart开始往前找字符t,找不到则退出,找得到的话更新子串信息。每次更新完子串信息之后和已经保存的最小串比较,如果
             当前子串更小则替换。
        */
        map<char,int> counter;//每一轮循环中属于T的字符串出现的次数
        map<char,int> counter_bk;//存放字符串T中各个字符出现的次数
        unordered_set<char>us_bk;//判断一个字符是否在T中出现的数据结构
        unordered_set<char>us;//辅助数据结构,在初始化counter的时候用到
        
        int minLen;
        int minStart=0;
        int minEnd=S.length()-1;
        int nextStart=S.length()-1;
        if(S.empty()||T.empty())return S;
        if(S.length()<T.length())return "";
        
        int i=0;
        while(i<T.length())
        {
            us_bk.insert(T[i]);i++;
        }
        
        unordered_set<char>::iterator iter;
        for(iter=us_bk.begin();iter!=us_bk.end();++iter)
        {
            counter_bk.insert(make_pair(*iter,0));
            counter.insert(make_pair(*iter,0));
        }
        
        i=0;
        while(i<T.length())
        {
            counter_bk[T[i]]+=1;i++;
        }
        
        i=S.length()-1;
        minEnd=-1;
        while(i>=0)
        {
            if(us_bk.count(S[i])==1)
            {
                if(minEnd==-1)minEnd=i;
                                  
                counter[S[i]]+=1;
                
                if(counter[S[i]]==counter_bk[S[i]]&&us.count(S[i])<1)us.insert(S[i]);
                
                if(us.size()==us_bk.size())
                {
                    minStart=i;
                    minLen=minEnd-minStart+1;break;
                }
            }     
            
            i--;
        }
        
        if(us.size()!=us_bk.size())return "";
        
        int nextLen=minLen;
        int nextMinStart=minStart;
        int nextMinEnd=minEnd;
        while(true)
        {
            int j=nextMinEnd-1;
            if(j<0)break;
            while(j>=0&&us_bk.count(S[j])==0)j--;
            if(j<0)break;
            //如果删除尾部字符之后导致尾部字符在子串中不出现或者出现的次数不够,需从串首往前搜寻
            if(counter[S[nextMinEnd]]==counter_bk[S[nextMinEnd]])
            {
                int k=nextMinStart-1;
                while(k>=0)
                {
                    if(us_bk.count(S[k])==1)
                    {
                        if(S[k]==S[nextMinEnd])
                        {
                            nextMinStart=k;
                            break;                        
                        }
                        else counter[S[k]]+=1;
                    }
                    k--;
                }
                
                if(k<0)break;
                
                nextMinEnd=j;
                nextLen=nextMinEnd-nextMinStart+1;
                if(nextLen<minLen)
                {
                    minStart=nextMinStart;minEnd=nextMinEnd;minLen=nextLen;
                }
                
            }
            
            //即便删除当前串的尾部字符,所得的子串也满足要求
            else
            {
                counter[S[nextMinEnd]]-=1;
                nextMinEnd=j;
                nextLen=nextMinEnd-nextMinStart+1;  
                if(nextLen<minLen)
                {
                    minStart=nextMinStart;
                    minEnd=nextMinEnd;
                    minLen=minEnd-minStart+1;
                }
            }            
        }
        
        return S.substr(minStart,minLen);
        
    }
};

结果如下:

image

posted @ 2013-08-27 00:01  曾见绝美的阳光  阅读(235)  评论(0编辑  收藏  举报