How to find smallest substring which contains all characters from a given string?

后端 未结 15 1038
灰色年华
灰色年华 2020-12-02 07:36

I have recently come across an interesting question on strings. Suppose you are given following:

Input string1: \"this is a test string\"
Input strin         


        
相关标签:
15条回答
  • 2020-12-02 08:12

    Here is Java implementation

    public static String shortestSubstrContainingAllChars(String input, String target) {
        int needToFind[] = new int[256];
        int hasFound[] = new int[256];
        int totalCharCount = 0;
        String result = null;
    
        char[] targetCharArray = target.toCharArray();
        for (int i = 0; i < targetCharArray.length; i++) {
            needToFind[targetCharArray[i]]++;           
        }
    
        char[] inputCharArray = input.toCharArray();
        for (int begin = 0, end = 0; end < inputCharArray.length; end++) {
    
            if (needToFind[inputCharArray[end]] == 0) {
                continue;
            }
    
            hasFound[inputCharArray[end]]++;
            if (hasFound[inputCharArray[end]] <= needToFind[inputCharArray[end]]) {
                totalCharCount ++;
            }
            if (totalCharCount == target.length()) {
                while (needToFind[inputCharArray[begin]] == 0 
                        || hasFound[inputCharArray[begin]] > needToFind[inputCharArray[begin]]) {
    
                    if (hasFound[inputCharArray[begin]] > needToFind[inputCharArray[begin]]) {
                        hasFound[inputCharArray[begin]]--;
                    }
                    begin++;
                }
    
                String substring = input.substring(begin, end + 1);
                if (result == null || result.length() > substring.length()) {
                    result = substring;
                }
            }
        }
        return result;
    }
    

    Here is the Junit Test

    @Test
    public void shortestSubstringContainingAllCharsTest() {
        String result = StringUtil.shortestSubstrContainingAllChars("acbbaca", "aba");
        assertThat(result, equalTo("baca"));
    
        result = StringUtil.shortestSubstrContainingAllChars("acbbADOBECODEBANCaca", "ABC");
        assertThat(result, equalTo("BANC"));
    
        result = StringUtil.shortestSubstrContainingAllChars("this is a test string", "tist");
        assertThat(result, equalTo("t stri"));
    }
    
    0 讨论(0)
  • 2020-12-02 08:12
    //[ShortestSubstring.java][1]
    
    public class ShortestSubstring {
    
        public static void main(String[] args) {
            String input1 = "My name is Fran";
            String input2 = "rim";
            System.out.println(getShortestSubstring(input1, input2));
        }
    
        private static String getShortestSubstring(String mainString, String toBeSearched) {
    
            int mainStringLength = mainString.length();
            int toBeSearchedLength = toBeSearched.length();
    
            if (toBeSearchedLength > mainStringLength) {
                throw new IllegalArgumentException("search string cannot be larger than main string");
            }
    
            for (int j = 0; j < mainStringLength; j++) {
                for (int i = 0; i <= mainStringLength - toBeSearchedLength; i++) {
                    String substring = mainString.substring(i, i + toBeSearchedLength);
                    if (checkIfMatchFound(substring, toBeSearched)) {
                        return substring;
                    }
                }
                toBeSearchedLength++;
            }
    
            return null;
        }
    
        private static boolean checkIfMatchFound(String substring, String toBeSearched) {
            char[] charArraySubstring = substring.toCharArray();
            char[] charArrayToBeSearched = toBeSearched.toCharArray();
            int count = 0;
    
            for (int i = 0; i < charArraySubstring.length; i++) {
                for (int j = 0; j < charArrayToBeSearched.length; j++) {
                    if (String.valueOf(charArraySubstring[i]).equalsIgnoreCase(String.valueOf(charArrayToBeSearched[j]))) {
                        count++;
                    }
                }
            }
            return count == charArrayToBeSearched.length;
        }
    }
    
    0 讨论(0)
  • 2020-12-02 08:17

    To see more details including working code, check my blog post at:

    http://www.leetcode.com/2010/11/finding-minimum-window-in-s-which.html

    To help illustrate this approach, I use an example: string1 = "acbbaca" and string2 = "aba". Here, we also use the term "window", which means a contiguous block of characters from string1 (could be interchanged with the term substring).

    alt text

    i) string1 = "acbbaca" and string2 = "aba".

    alt text

    ii) The first minimum window is found. Notice that we cannot advance begin pointer as hasFound['a'] == needToFind['a'] == 2. Advancing would mean breaking the constraint.

    alt text

    iii) The second window is found. begin pointer still points to the first element 'a'. hasFound['a'] (3) is greater than needToFind['a'] (2). We decrement hasFound['a'] by one and advance begin pointer to the right.

    alt text

    iv) We skip 'c' since it is not found in string2. Begin pointer now points to 'b'. hasFound['b'] (2) is greater than needToFind['b'] (1). We decrement hasFound['b'] by one and advance begin pointer to the right.

    alt text

    v) Begin pointer now points to the next 'b'. hasFound['b'] (1) is equal to needToFind['b'] (1). We stop immediately and this is our newly found minimum window.

    The idea is mainly based on the help of two pointers (begin and end position of the window) and two tables (needToFind and hasFound) while traversing string1. needToFind stores the total count of a character in string2 and hasFound stores the total count of a character met so far. We also use a count variable to store the total characters in string2 that's met so far (not counting characters where hasFound[x] exceeds needToFind[x]). When count equals string2's length, we know a valid window is found.

    Each time we advance the end pointer (pointing to an element x), we increment hasFound[x] by one. We also increment count by one if hasFound[x] is less than or equal to needToFind[x]. Why? When the constraint is met (that is, count equals to string2's size), we immediately advance begin pointer as far right as possible while maintaining the constraint.

    How do we check if it is maintaining the constraint? Assume that begin points to an element x, we check if hasFound[x] is greater than needToFind[x]. If it is, we can decrement hasFound[x] by one and advancing begin pointer without breaking the constraint. On the other hand, if it is not, we stop immediately as advancing begin pointer breaks the window constraint.

    Finally, we check if the minimum window length is less than the current minimum. Update the current minimum if a new minimum is found.

    Essentially, the algorithm finds the first window that satisfies the constraint, then continue maintaining the constraint throughout.

    0 讨论(0)
  • 2020-12-02 08:17

    JavaScript solution in bruteforce way:

    function shortestSubStringOfUniqueChars(s){
    	var uniqueArr = [];
    	for(let i=0; i<s.length; i++){
    		if(uniqueArr.indexOf(s.charAt(i)) <0){
    			uniqueArr.push(s.charAt(i));
    		}
    	}
    
    	let windoww = uniqueArr.length;
    
    	while(windoww < s.length){
    		for(let i=0; i<s.length - windoww; i++){
    			let match = true;
    			let tempArr = [];
    			for(let j=0; j<uniqueArr.length; j++){
    				if(uniqueArr.indexOf(s.charAt(i+j))<0){
    					match = false;
    					break;
    				}
    			}
    		let checkStr
    		if(match){
    			checkStr =  s.substr(i, windoww);
    			for(let j=0; j<uniqueArr.length; j++){
    				if(uniqueArr.indexOf(checkStr.charAt(j))<0){
    					match = false;
    					break;
    				}
    			}
    		}
    		if(match){
    		    return checkStr;
    		}
     	 }
     	 windoww = windoww + 1;
    	}
    }
    
    console.log(shortestSubStringOfUniqueChars("ABA"));

    0 讨论(0)
  • 2020-12-02 08:21

    Please have a look at this as well:

    //-----------------------------------------------------------------------
    
    bool IsInSet(char ch, char* cSet)
    {
        char* cSetptr = cSet;
        int index = 0;
        while (*(cSet+ index) != '\0')
        {
            if(ch == *(cSet+ index))
            {
                return true;            
            }
            ++index;
        }
        return false;
    }
    
    void removeChar(char ch, char* cSet)
    {
        bool bShift = false;
        int index = 0;
        while (*(cSet + index) != '\0')
        {
            if( (ch == *(cSet + index)) || bShift)
            {
                *(cSet + index) = *(cSet + index + 1);
                bShift = true;
            }
            ++index;
        }
    }
    typedef struct subStr
    {
        short iStart;
        short iEnd;
        short szStr;
    }ss;
    
    char* subStringSmallest(char* testStr, char* cSet)
    {
        char* subString = NULL;
        int iSzSet = strlen(cSet) + 1;
        int iSzString = strlen(testStr)+ 1;
        char* cSetBackUp = new char[iSzSet];
        memcpy((void*)cSetBackUp, (void*)cSet, iSzSet);
    
        int iStartIndx = -1;    
        int iEndIndx = -1;
        int iIndexStartNext = -1;
    
        std::vector<ss> subStrVec;
        int index = 0;
    
        while( *(testStr+index) != '\0' )
        {
            if (IsInSet(*(testStr+index), cSetBackUp))
            {
                removeChar(*(testStr+index), cSetBackUp);
    
                if(iStartIndx < 0)
                {
                    iStartIndx = index;
                }
                else if( iIndexStartNext < 0)
                    iIndexStartNext = index;
                else
                    ;
    
                if (strlen(cSetBackUp) == 0 )
                {
                    iEndIndx = index;
                    if( iIndexStartNext == -1)
                        break;
                    else
                    {
                        index = iIndexStartNext;
                        ss stemp = {iStartIndx, iEndIndx, (iEndIndx-iStartIndx + 1)};
                        subStrVec.push_back(stemp);
                        iStartIndx = iEndIndx = iIndexStartNext = -1;
                        memcpy((void*)cSetBackUp, (void*)cSet, iSzSet);
                        continue;
                    }
                }
            }
            else
            {
                if (IsInSet(*(testStr+index), cSet))
                {
                    if(iIndexStartNext < 0)
                        iIndexStartNext = index;
                }
            }
    
            ++index;
        }
    
    
        int indexSmallest = 0;
        for(int indexVec = 0; indexVec < subStrVec.size(); ++indexVec)
        {
            if(subStrVec[indexSmallest].szStr > subStrVec[indexVec].szStr)
                indexSmallest = indexVec;       
        }
    
        subString = new char[(subStrVec[indexSmallest].szStr) + 1];
        memcpy((void*)subString, (void*)(testStr+ subStrVec[indexSmallest].iStart), subStrVec[indexSmallest].szStr);
        memset((void*)(subString + subStrVec[indexSmallest].szStr), 0, 1);
    
        delete[] cSetBackUp;
        return subString;
    }
    //--------------------------------------------------------------------
    
    0 讨论(0)
  • 2020-12-02 08:25

    I received the same interview question. I am a C++ candidate but I was in a position to code relatively fast in JAVA.

    Java [Courtesy : Sumod Mathilakath]

    import java.io.*;
    import  java.util.*;
    
    class UserMainCode
    {
    
    
        public String GetSubString(String input1,String input2){
            // Write code here...
            return find(input1, input2);
        }
      private static boolean containsPatternChar(int[] sCount, int[] pCount) {
            for(int i=0;i<256;i++) {
                if(pCount[i]>sCount[i])
                    return false;
            }
            return true;
        }
      public static String find(String s, String p) {
            if (p.length() > s.length())
                return null;
            int[] pCount = new int[256];
            int[] sCount = new int[256];
            // Time: O(p.lenght)
            for(int i=0;i<p.length();i++) {
                pCount[(int)(p.charAt(i))]++;
                sCount[(int)(s.charAt(i))]++;
            }
            int i = 0, j = p.length(), min = Integer.MAX_VALUE;
            String res = null;
            // Time: O(s.lenght)
            while (j < s.length()) {
                if (containsPatternChar(sCount, pCount)) {
                    if ((j - i) < min) {
                        min = j - i;
                        res = s.substring(i, j);
                        // This is the smallest possible substring.
                        if(min==p.length())
                            break;
                        // Reduce the window size.
                        sCount[(int)(s.charAt(i))]--;
                        i++;
                    }
                } else {
                    sCount[(int)(s.charAt(j))]++;
                    // Increase the window size.
                    j++;
                }
            }
            System.out.println(res);
            return res;
        }
    }
    

    C++ [Courtesy : sundeepblue]

    #include <iostream>
    #include <vector>
    #include <string>
    #include <climits>
    using namespace std;
    string find_minimum_window(string s, string t) {
        if(s.empty() || t.empty()) return;
    
        int ns = s.size(), nt = t.size();
        vector<int> total(256, 0);
        vector<int> sofar(256, 0);
        for(int i=0; i<nt; i++) 
            total[t[i]]++;
    
        int L = 0, R; 
        int minL = 0;                           //gist2
        int count = 0;
        int min_win_len = INT_MAX;
    
        for(R=0; R<ns; R++) {                   // gist0, a big for loop
            if(total[s[R]] == 0) continue;
            else sofar[s[R]]++;
    
            if(sofar[s[R]] <= total[s[R]])      // gist1, <= not <
                count++;
    
            if(count == nt) {                   // POS1
                while(true) {
                    char c = s[L]; 
                    if(total[c] == 0) { L++; }
                    else if(sofar[c] > total[c]) {
                        sofar[c]--;
                        L++;
                    }
                    else break;
                }  
                if(R - L + 1 < min_win_len) {   // this judge should be inside POS1
                    min_win_len = R - L + 1;
                    minL = L;
                }
            }
        }
        string res;
        if(count == nt)                         // gist3, cannot forget this. 
            res = s.substr(minL, min_win_len);  // gist4, start from "minL" not "L"
        return res;
    }
    int main() {
        string s = "abdccdedca";
        cout << find_minimum_window(s, "acd");
    }
    

    Erlang [Courtesy : wardbekker]

    -module(leetcode).
    
    -export([min_window/0]).
    
    %% 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.
    
    
    
    min_window() ->
        "eca" = min_window("cabeca", "cae"),
        "eca" = min_window("cfabeca", "cae"),
        "aec" = min_window("cabefgecdaecf", "cae"),
        "cwae" = min_window("cabwefgewcwaefcf", "cae"),
        "BANC" = min_window("ADOBECODEBANC", "ABC"),
        ok.
    
    min_window(T, S) ->
        min_window(T, S, []).
    
    min_window([], _T, MinWindow) ->
        MinWindow;
    min_window([H | Rest], T, MinWindow) ->
        NewMinWindow = case lists:member(H, T) of
                           true ->
                               MinWindowFound = fullfill_window(Rest, lists:delete(H, T), [H]),
                               case length(MinWindow) == 0 orelse (length(MinWindow) > length(MinWindowFound)
                                   andalso length(MinWindowFound) > 0) of
                                   true ->
                                       MinWindowFound;
                                   false ->
                                       MinWindow
                               end;
                           false ->
                               MinWindow
                       end,
        min_window(Rest, T, NewMinWindow).
    
    fullfill_window(_, [], Acc) ->
        %% window completed
        Acc;
    fullfill_window([], _T, _Acc) ->
        %% no window found
        "";
    fullfill_window([H | Rest], T, Acc) ->
        %% completing window
        case lists:member(H, T) of
            true ->
                fullfill_window(Rest, lists:delete(H, T), Acc ++ [H]);
            false ->
                fullfill_window(Rest, T, Acc ++ [H])
        end.
    

    REF:

    • http://articles.leetcode.com/finding-minimum-window-in-s-which/#comment-511216
    • http://www.mif.vu.lt/~valdas/ALGORITMAI/LITERATURA/Cormen/Cormen.pdf
    0 讨论(0)
提交回复
热议问题