1. 程式人生 > >Lintcode 32 Minimum Window Substring

Lintcode 32 Minimum Window Substring

題目描述

Description
Given a string source and a string target, find the minimum window in source which will contain all the characters in target.

  • If there is no such window in source that covers all characters in target, return the emtpy string “”.
  • If there are multiple such windows, you are guaranteed that there will always be only one unique minimum window in source.
  • The target string may contain duplicate characters, the minimum window should cover all characters including the duplicate characters in target.

Clarification
Should the characters in minimum window has the same order in target?

  • Not necessary.

Example
For source = “ADOBECODEBANC”, target = “ABC”, the minimum window is “BANC”

Challenge
Can you do it in time complexity O(n) ?
題目意思是給定字串source和目標字串target,在字串source中找到包括所有目標字串字母的最小子串。

思路

剛開始我想用迴圈來掃描,然後用replacefirst來替換掉重複的(可能難以表述清楚),不過這樣也做出來了,不過明顯不好理解,所以就換了一種方法寫(最開始的程式碼後面也會貼出來)
之後呢,思考了下,認為用hashmap來做會比較方便,而且便於理解,大致思路如下
用一個HashMap<Character, Integer>來存放每個字元,key是字元,value值為map中字元的個數

  • 首先將target字串中每個字元存放進map裡面
for (int i = 0; i < tSize; i++) {
            char c = target.charAt(i);
            if (map.containsKey(c)) {
                map.put(c, map.get(c) + 1);
            } else {
                map.put(c, 1);
            }
        }
  • 之後迴圈找到符合條件的字串(包含目標字串中所有字元的字串)
while (j < sSize && n < tSize) {
                    char c = source.charAt(j);
                    if (map.containsKey(c)) {
                        if (map.get(c) > 0) {
                            n++;
                        }
                        map.put(c, map.get(c) - 1);
                    }
                    j++;
                }
  • 然後再根據字串長度來替換result字串(最終字串)
if (n == tSize && min > j - i) {
                    min = j - i;
                    result = source.substring(i, j);
                }
  • 最後再判斷是否要將字元放回去
char c = source.charAt(i);
                if (map.containsKey(c)) {
                    if (map.get(c) >= 0) {
                        n--;
                    }
                    map.put(c, map.get(c) + 1);
                }

程式碼

  • 最開始的解法
public class Solution {
    /**
     * @param source : A string
     * @param target: A string
     * @return: A string denote the minimum window, return "" if there is no such a string
     */
    public String minWindow(String source, String target) {
        // write your code here
        if (source.length() == 0 || target.length() == 0) {
            return source;
        }
        String str;
        int min = source.length();
        int start = 0, end = 0;
        int ssize = source.length() - target.length() + 1;
        for (int i = 0; i < ssize; i++) {
            String t = target;
            str = "";
            if (t.contains(String.valueOf(source.charAt(i)))) {
                for (int j = i; j < min + i && j < source.length(); j++) {
                    String temp = String.valueOf(source.charAt(j));
                    if (t.contains(temp)) {
                        str += temp;
                        if ("*&^%$#@!():".contains(temp)) {
                            temp = "//" + temp;
                        }
                        t = t.replaceFirst(temp,"");
                    }
                    if (str.length() == target.length() && min >= j - i + 1) {
                        min = j - i + 1;
                        start = i;
                        end = j + 1;
                        break;
                    }
                }
            }
        }
        return source.substring(start, end);
    }
}
  • hashmap解法
public String minWindow(String source, String target) {
        // write your code here
        //第一種解法用hashmap
        HashMap<Character, Integer> map = new HashMap<>();
        int tSize = target.length();
        int sSize = source.length();
        //將target字串中所有的字元放入map中
        for (int i = 0; i < tSize; i++) {
            char c = target.charAt(i);
            if (map.containsKey(c)) {
                map.put(c, map.get(c) + 1);
            } else {
                map.put(c, 1);
            }
        }
        String result = "";
        int min = Integer.MAX_VALUE;
        int n = 0, j = 0;
        for (int i = 0; i < sSize; i++) {
            if (map.containsKey(source.charAt(i))) {
                //找到符合條件的子串
                while (j < sSize && n < tSize) {
                    char c = source.charAt(j);
                    if (map.containsKey(c)) {
                        if (map.get(c) > 0) {
                            n++;
                        }
                        map.put(c, map.get(c) - 1);
                    }
                    j++;
                }
                //替換result
                if (n == tSize && min > j - i) {
                    min = j - i;
                    result = source.substring(i, j);
                }
                //判斷是否要將第一個字元重新放回map中
                char c = source.charAt(i);
                if (map.containsKey(c)) {
                    if (map.get(c) >= 0) {
                        n--;
                    }
                    map.put(c, map.get(c) + 1);
                }
            }
        }
        return result;
    }

還有一種解法,是不用hashmap的,而是用大小為256(正好對應char字元)陣列儲存字元,大體思路差不多,這個更好理解吧

  • 不用hashmap的解法
public String minWindow(String source, String target) {
        // write your code here
        //第二種解法不用hashmap
        int tSize = target.length();
        int sSize = source.length();
        //將target裡面的每個字元放入陣列中
        for (int i = 0; i < tSize; i++) {
            chars[target.charAt(i)]++;
        }
        String result = "";
        int j = 0, min = Integer.MAX_VALUE;
        for (int i = 0; i < sSize; i++) {
            while (j < sSize && !isFindAll()) {
                chars[source.charAt(j)]--;
                j++;
            }
            if (isFindAll() && j - i < min) {
                min = j - i;
                result = source.substring(i, j);
            }
            chars[source.charAt(i)]++;
        }
        return result;
    }

    //判斷是否所有字元都被尋找到了
    private boolean isFindAll() {
        for (int i = 0; i < chars.length; i++) {
            if (chars[i] > 0) {
                return false;
            }
        }
        return true;
    }