Saturday, April 28, 2012

Minimum Window Sub-string (a.k.a Sliding Window) -- LeetCode 76

[Question]

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".
[Analysis]
The problem can be resolved by using a window (tail, head) on string S.
1) Both indexes tail and head start from index 0 of S.
2) Move "head" forward step by step in S until find a window (tail, head) contain all the characters in T.
3) Then move "tail" forward to leave characters (in T) out of window, as many as possible until the sub-string in window cannot contain all characters in T.
4) Repeat 2, 3 until reach the end of S. Meanwhile, measure the length of window and record the minimum window as requested.

The time complexity is O(N).
[Solution]
//
//--- C++ ---
//
class Solution {
public:
    string minWindow(string s, string t) {
        unordered_map<char, int> target;
        for (auto c: t) 
            target[c]++;
            
        unordered_map<char, int> counter;
        auto contained=[&]() {
            for (auto& t: target) 
                if (t.second>counter[t.first]) return false;
            return true;
        };
        
        int i=0, j=0;
        int ri=0, rj=INT_MAX;
        while (j<s.size()) {
            while (!contained() && j<s.size()) 
                counter[s[j++]]++;
            
            while(contained() && i<j) {
                if (j-i<rj-ri)
                    ri=i, rj=j;
                counter[s[i++]]--;   
            }
        }
        return rj-ri<=s.size()?s.substr(ri, rj-ri):"";
    }
};
//
//--- Java ---
//
public class MinimumWindowString implements TestCaseRunner {

    public String minWindow(String S, String T) {
        if (S.length() < T.length() || T.length()==0) return "";
        String minStr = S+" ";
        S.toUpperCase();
        T.toUpperCase();

       // -- cnt arrays used for judging whether all characters 
       //  in target string are included in window sub-string.
        int[] wndCnts = new int[26];
        int[] tgtCnts = new int[26];
     
        char[] temp = T.toCharArray();
        for (char c: temp) {
            tgtCnts[c-'A'] ++;
        }
     
        int loadFlag = setFlag(T);
        int loadFlagCopy = loadFlag;
     
        int head, tail;
        head = tail =0;
     
        // -- skip letters that not meaningful --
        while (!charInTarget(S.charAt(head), tgtCnts)) {
        head++;
        };      
        tail = head;
     
        // -- start looping --
        while (head < S.length() ) {
            while (!charInTarget(S.charAt(tail), tgtCnts)) {
            tail++;
            };
         
            // -- grow to the match --
            while (head < S.length() && loadFlag!=0) {
                char c= S.charAt(head++);
                if (charInTarget(c, tgtCnts)) {
                    wndCnts[c-'A']++;
                    if (wndCnts[c-'A']==tgtCnts[c-'A']) {
                        loadFlag ^= 1<<(c-'A');
                    }
                }              
            }

            if (loadFlag == 0) {
                if (minStr.length()>head-tail) {
                    minStr = S.substring(tail, head);
                }
            }
            else break;
         
            // -- reduce the match --
            int unloadFlag = 0;
            while(tail<head && unloadFlag==0) {
                char c= S.charAt(tail++);
                if (charInTarget(c, tgtCnts)) {
                    wndCnts[c-'A']--;
                    if (wndCnts[c-'A']<tgtCnts[c-'A']) {
                        unloadFlag = 1<<(c-'A');
                    }
                    else if (minStr.length()>head-tail) {
                        minStr = S.substring(tail, head);
                    }
                }
            }
         
            loadFlag = loadFlagCopy & unloadFlag;
        }
     
        if (minStr.equals(S+" ")) return "";
        return minStr;
    }
 
    int setFlag (String s) {
        char[] chars = s.toCharArray();
        int a = 0;
        for (char c: chars) {
            a |= 1<<(c-'A');
        }
        return a;
    }
 
    boolean charInTarget (char c, int[] target) {
        return (target[c-'A']>0);
    }
 
    public void runTestCases() {
String[] S = {"ADOBECODEBANC", "ABBADBABDRGBACCCB", "HBBHHCADBEBCAECBCCBB"};
String[] T = {"ABC", "ABBC", "ABBC"};

for (int i=0; i<S.length; i++) {
String ret = minWindow(S[i], T[i]);
System.out.println(ret);
}
    }
}


2 comments:

  1. Dude, have you tested online by leetcode? I had the essentially same logic but couldn't pass large data set.

    ReplyDelete
  2. Hi, Jason, I did not use leetcode to test this. I used my own data and test program.

    ReplyDelete