雙指針 子字符串問題

  LeetCode 76. 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 empty string “”.
If there are multiple such windows, you are guaranteed that there will always be only one unique minimum window in S.

  其實仔細思考一下感覺應該是可以思考出方法的纔對。至少一上來就能感覺到是雙指針的題。
  一般來說,給定一個字符串或序列,要求低複雜度找出其中某個子序列,往往可以考慮雙指針方法

  雙指針方案注意點:

  1. 第一個指針就是簡單遍歷 for(int i = 0; i<arr.length; i++)。關鍵是第二個指針,第二個指針可能是for(int j = 0; j<i; j++) 這種;也可能是滿足某些條件時,執行j++j-- 等,。具體方案視題意千變萬化。
  2. 雙指針的目標可能是區間[left, right],而這個區間可能通過這幾種途徑獲得:可能是區間[j,i] ,也可能是區間[0,j]和[j+1,i], 再或者是一次次遍歷中某個計數值count的逐漸達到或又遠離target
  3. 有時候當滿足非最優的題設目標時,可以獲得一個區間[j,i],並記錄下此時的左右邊界leftright 或者其他計算結果。而爲了獲得更優的題設目標,將改變 ji ,繼續遍歷。

本題代碼如下:

public String minWindow(String s, String t) {
    if(s == null || s.length() < t.length() || s.length() == 0){
        return "";
    }
    HashMap<Character,Integer> map = new HashMap<Character,Integer>();
    for(char c : t.toCharArray()){
        if(map.containsKey(c)){
            map.put(c,map.get(c)+1);
        }else{
            map.put(c,1);
        }
    }
    int left = 0;
    int minLeft = 0;
    int minLen = s.length()+1;
    int count = 0;
    for(int right = 0; right < s.length(); right++){
        if(map.containsKey(s.charAt(right))){
            //如果map裏有s當前在right處可以提供給t的字符
            //就將s的字符提供給t,t在該字符處的需求-1
            map.put(s.charAt(right),map.get(s.charAt(right))-1);

            //每解決t的一個字符需求,將count計數+1,count==t.length()時表示找到一個符合要求的區間
            if(map.get(s.charAt(right)) >= 0){
                count ++;
            }
            //雙指針的目標可能一次次遍歷中某個計數值count的逐漸達到或又遠離target
            //每解決完一次t的完整字符需求,就更新一次區間左指針和區間長度
            while(count == t.length()){
                if(right-left+1 < minLen){
                    minLeft = left;
                    minLen = right-left+1;
                }            
                //將本輪找到的區間中最左邊的一個t所需字符剔除,以便左右指針可以繼續移動
                if(map.containsKey(s.charAt(left))){
                    map.put(s.charAt(left),map.get(s.charAt(left))+1);
                    if(map.get(s.charAt(left)) > 0){
                        count --;
                    }
                }
                //滿足某些條件時,執行 j++ 或 j-- 
                left ++ ;
            }
        }
    }
    if(minLen>s.length())  
        return "";

    return s.substring(minLeft,minLeft+minLen);        
}

  引用附上使用雙指針和Map解決尋找子字符串問題的解決模板

  非常厲害!

The code of solving this problem is below. It might be the shortest among all solutions provided in Discuss.

string minWindow(string s, string t) {
        vector<int> map(128,0);
        for(auto c: t) map[c]++;
        int counter=t.size(), begin=0, end=0, d=INT_MAX, head=0;
        while(end<s.size()){
            if(map[s[end++]]-->0) counter--; //in t
            while(counter==0){ //valid
                if(end-begin<d)  d=end-(head=begin);
                if(map[s[begin++]]++==0) counter++;  //make it invalid
            }  
        }
        return d==INT_MAX? "":s.substr(head, d);
    }

Here comes the template.

For most substring problem, we are given a string and need to find a substring of it which satisfy some restrictions. A general way is to use a hashmap assisted with two pointers. The template is given below.

int findSubstring(string s){
        vector<int> map(128,0);
        int counter; // check whether the substring is valid
        int begin=0, end=0; //two pointers, one point to tail and one  head
        int d; //the length of substring

        for() { /* initialize the hash map here */ }

        while(end<s.size()){

            if(map[s[end++]]-- ?){  /* modify counter here */ }

            while(/* counter condition */){ 

                 /* update d here if finding minimum*/

                //increase begin to make it invalid/valid again

                if(map[s[begin++]]++ ?){ /*modify counter here*/ }
            }  

            /* update d here if finding maximum*/
        }
        return d;
  }

One thing needs to be mentioned is that when asked to find maximum substring, we should update maximum after the inner while loop to guarantee that the substring is valid. On the other hand, when asked to find minimum substring, we should update minimum inside the inner while loop.

The code of solving Longest Substring with At Most Two Distinct Characters is below:

int lengthOfLongestSubstringTwoDistinct(string s) {
        vector<int> map(128, 0);
        int counter=0, begin=0, end=0, d=0; 
        while(end<s.size()){
            if(map[s[end++]]++==0) counter++;
            while(counter>2) if(map[s[begin++]]--==1) counter--;
            d=max(d, end-begin);
        }
        return d;
    }

The code of solving Longest Substring Without Repeating Characters is below:

Update 01.04.2016, thanks @weiyi3 for advise.

int lengthOfLongestSubstring(string s) {
        vector<int> map(128,0);
        int counter=0, begin=0, end=0, d=0; 
        while(end<s.size()){
            if(map[s[end++]]++>0) counter++; 
            while(counter>0) if(map[s[begin++]]-->1) counter--;
            d=max(d, end-begin); //while valid, update d
        }
        return d;
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章