Friday, October 26, 2018

686. Repeated String Match

686Repeated String Match
Given two strings A and B, find the minimum number of times A has to be repeated such that B is a substring of it. If no such solution, return -1.
For example, with A = "abcd" and B = "cdabcdab".
Return 3, because by repeating A three times (“abcdabcdabcd”), B is a substring of it; and B is not a substring of A repeated two times ("abcdabcd").
Note:
The length of A and B will be between 1 and 10000.
--------------------
为满足包含B的条件,A最多重复的次数不会超过B.length() + A.length() * 2

Solution #1, 暴力解
注意返回时的count的计算
O(m * n)
class Solution {
    public int repeatedStringMatch(String A, String B) {
        String s = A;
        while (s.length() <= B.length() + A.length()) {
            s += A;
        }
        
        for (int i = 0; i < s.length() - B.length() + 1; i++) {
            String sub = s.substring(i, i + B.length());
            if (sub.equals(B)) {
                return (i + B.length() + A.length() - 1) / A.length();
            }
        }
    
        return -1;
    }
}

Solution #2, 典型的KMP
理解的要点
1. 找每个点的longest proper preffix == longest proper suffix (proper的意思是string本身不能算*ffix)
2. preprocess里是pattern自己比自己。主方法里是pattern对比A。2者的原理其实是一样的。
3. table里存的是proper的长度

class Solution {
    public int repeatedStringMatch(String A, String pattern) {
        int i = 0, j = 0;
        int[] table = preprocess(pattern);
        
        String s = A;
        while (s.length() < pattern.length() + A.length()) {
            s += A;
        }
        
        while (i < s.length()) {
            if (s.charAt(i) == pattern.charAt(j)) {
                j++;
                i++;
                if (j == pattern.length()) break;
            }else if (j > 0) {
                j = table[j - 1];
            }else {
                i++;
            }
        }
        System.out.println(i);
        if (i == s.length()) return -1;
        return (i + A.length() - 1) / A.length();
    }
    
    private int[] preprocess(String s) {
        int[] table = new int[s.length()];
        int j = 0, i = 1;

        while (i < s.length()) {
            if (s.charAt(j) == s.charAt(i)) {
                table[i] = j + 1;
                j++;
                i++;
            }else if (j > 0) {
                j = table[j - 1];
            }else {
                i++;
            }
        }
        
        return table;
    }
}

Tuesday, October 23, 2018

299. Bulls and Cows

299Bulls and Cows
You are playing the following Bulls and Cows game with your friend: You write down a number and ask your friend to guess what the number is. Each time your friend makes a guess, you provide a hint that indicates how many digits in said guess match your secret number exactly in both digit and position (called "bulls") and how many digits match the secret number but locate in the wrong position (called "cows"). Your friend will use successive guesses and hints to eventually derive the secret number.
Write a function to return a hint according to the secret number and friend's guess, use A to indicate the bulls and B to indicate the cows. 
Please note that both secret number and friend's guess may contain duplicate digits.
Example 1:
Input: secret = "1807", guess = "7810"

Output: "1A3B"

Explanation: 1 bull and 3 cows. The bull is 8, the cows are 0, 1 and 7.
Example 2:
Input: secret = "1123", guess = "0111"

Output: "1A1B"

Explanation: The 1st 1 in friend's guess is a bull, the 2nd or 3rd 1 is a cow.
Note: You may assume that the secret number and your friend's guess only contain digits, and their lengths are always equal.
-------------------------
*注意想好所有的case再下笔写
class Solution {
    public String getHint(String secret, String guess) {
        int a = 0, b = 0;
        Map<Character, Integer> counts = getCounts(secret);
        Map<Character, Integer> wasted = new HashMap<>();
        
        for (int i = 0; i < guess.length(); i++) {
            char c = guess.charAt(i);
            if (c == secret.charAt(i)) {
                if (counts.get(c) > 0) {
                    a++;
                    counts.put(c, counts.get(c) - 1);
                }else if (wasted.get(c) > 0) {
                    a++;
                    b--;
                    wasted.put(c, wasted.get(c) - 1);
                }
            } else if (counts.containsKey(c) && counts.get(c) > 0) {
                b++;
                counts.put(c, counts.get(c) - 1);
                wasted.put(c, wasted.getOrDefault(c, 0) + 1);
            }
        }
        
        return a + "A" + b + "B";
    }
    
    private Map<Character, Integer> getCounts(String s) {
        Map<Character, Integer> counts = new HashMap<>();
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            counts.put(c, counts.getOrDefault(c, 0) + 1);
        }
        
        return counts;
    }
}

803. Bricks Falling When Hit

803Bricks Falling When Hit
We have a grid of 1s and 0s; the 1s in a cell represent bricks.  A brick will not drop if and only if it is directly connected to the top of the grid, or at least one of its (4-way) adjacent bricks will not drop.
We will do some erasures sequentially. Each time we want to do the erasure at the location (i, j), the brick (if it exists) on that location will disappear, and then some other bricks may drop because of that erasure.
Return an array representing the number of bricks that will drop after each erasure in sequence.
Example 1:
Input: 
grid = [[1,0,0,0],[1,1,1,0]]
hits = [[1,0]]
Output: [2]
Explanation: 
If we erase the brick at (1, 0), the brick at (1, 1) and (1, 2) will drop. So we should return 2.
Example 2:
Input: 
grid = [[1,0,0,0],[1,1,0,0]]
hits = [[1,1],[1,0]]
Output: [0,0]
Explanation: 
When we erase the brick at (1, 0), the brick at (1, 1) has already disappeared due to the last move. So each erasure will cause no bricks dropping.  Note that the erased brick (1, 0) will not be counted as a dropped brick.

Note:
  • The number of rows and columns in the grid will be in the range [1, 200].
  • The number of erasures will not exceed the area of the grid.
  • It is guaranteed that each erasure will be different from any other erasure, and located inside the grid.
  • An erasure may refer to a location with no brick - if it does, no bricks drop.
-------------------
第一次遍历,踢掉所有hit
第二次遍历,确定最终不掉的砖头
第三次反向遍历hits[],把hit一个个加回去。每加一个,检查是否跟不掉砖头有连接

每个点最多被跑2次,O(N), N为矩阵的大小
class Solution {
    public int[] hitBricks(int[][] grid, int[][] hits) {
        removeHits(grid, hits);
        int id = 2;
        for (int col = 0; col < grid[0].length; col++) {
            dfs(grid, 0, col, id);
        }
        
        int[] rt = new int[hits.length];
        for (int i = hits.length - 1; i >= 0; i--) {
            int row = hits[i][0], col = hits[i][1];
            if (grid[row][col] == 0) {
                grid[row][col] = 1;
                if (checkContact(grid, row, col)) {
                    rt[i] = dfs(grid, row, col, 2) - 1;
                }
            } 
        }
        
        return rt;
    }
    
    private boolean checkContact(int[][] grid, int row, int col) {
        if (row == 0) return true;
        if (row - 1 >= 0 && grid[row - 1][col] == 2) return true;
        if (row + 1 < grid.length && grid[row + 1][col] == 2) return true;
        if (col - 1 >= 0 && grid[row][col - 1] == 2) return true;
        if (col + 1 < grid[0].length && grid[row][col + 1] == 2) return true;
        
        return false;
    }
    
    private int dfs(int[][] grid, int row, int col, int id) {
        if (row < 0 || row >= grid.length || col < 0 || col >= grid[0].length 
            || grid[row][col] != 1) return 0;
        grid[row][col] = id;
        
        return 1 + dfs(grid, row + 1, col, id) + dfs(grid, row - 1, col, id) 
            + dfs(grid, row, col + 1, id) + dfs(grid, row, col - 1, id);
    }
    
    private void removeHits(int[][] grid, int[][] hits) {
        for (int[] hit : hits) {
            if (grid[hit[0]][hit[1]] == 0) grid[hit[0]][hit[1]] = -1;
            else grid[hit[0]][hit[1]] = 0;
        }
    }
}

Sunday, October 21, 2018

844. Backspace String Compare

844Backspace String Compare
Given two strings S and T, return if they are equal when both are typed into empty text editors. # means a backspace character.
Example 1:
Input: S = "ab#c", T = "ad#c"
Output: true
Explanation: Both S and T become "ac".
Example 2:
Input: S = "ab##", T = "c#d#"
Output: true
Explanation: Both S and T become "".
Example 3:
Input: S = "a##c", T = "#a#c"
Output: true
Explanation: Both S and T become "c".
Example 4:
Input: S = "a#c", T = "b"
Output: false
Explanation: S becomes "c" while T becomes "b".
Note:
  1. 1 <= S.length <= 200
  2. 1 <= T.length <= 200
  3. S and T only contain lowercase letters and '#' characters.
Follow up:
  • Can you solve it in O(N) time and O(1) space?
-----------------
内部的while循环每次找到下一个非'#'的index

class Solution {
    public boolean backspaceCompare(String S, String T) {
        int m = S.length(), n = T.length();
        int i = m - 1, j = n - 1;
        int count1 = 0, count2 = 0;
        
        while (i >= 0 || j >= 0) {
            
            while (i >= 0) {
                if (S.charAt(i) == '#') {
                    count1++;
                    i--;
                }else if (count1 > 0) {
                    count1--;
                    i--;
                }else break;
            }
            
            while (j >= 0) {
                if (T.charAt(j) == '#') {
                    count2++;
                    j--;
                }else if (count2 > 0) {
                    count2--;
                    j--;
                }else break;
                
            }
            if (i >= 0 && j >= 0 && S.charAt(i) != T.charAt(j)) return false;
            if (i < 0 && j >= 0) return false;
            if (i >= 0 && j < 0) return false;
            i--;
            j--;
        }
        
        return true;
    }
}

Saturday, October 20, 2018

857. Minimum Cost to Hire K Workers

857Minimum Cost to Hire K Workers
There are N workers.  The i-th worker has a quality[i] and a minimum wage expectation wage[i].
Now we want to hire exactly K workers to form a paid group.  When hiring a group of K workers, we must pay them according to the following rules:
  1. Every worker in the paid group should be paid in the ratio of their quality compared to other workers in the paid group.
  2. Every worker in the paid group must be paid at least their minimum wage expectation.
Return the least amount of money needed to form a paid group satisfying the above conditions.

    Example 1:
    Input: quality = [10,20,5], wage = [70,50,30], K = 2
    Output: 105.00000
    Explanation: We pay 70 to 0-th worker and 35 to 2-th worker.
    
    Example 2:
    Input: quality = [3,1,10,10,1], wage = [4,8,2,2,7], K = 3
    Output: 30.66667
    Explanation: We pay 4 to 0-th worker, 13.33333 to 2-th and 3-th workers seperately. 
    

    Note:
    1. 1 <= K <= N <= 10000, where N = quality.length = wage.length
    2. 1 <= quality[i] <= 10000
    3. 1 <= wage[i] <= 10000
    4. Answers within 10^-5 of the correct answer will be considered correct.
    ----------------------
    找合适的长度为k的subset。遍历整个array,每次找以[i]为baseline的符合条件的subset
    O(NlogN)。注意double比较的写法。sort的时候要同时sort 3个array

    class Solution {
        public double mincostToHireWorkers(int[] quality, int[] wage, int k) {
            int n = quality.length;
            Person[] person = new Person[n];
            for (int i = 0; i < n; i++) {
                person[i] = new Person(quality[i], wage[i]);
            }        
            Arrays.sort(person, (a, b) -> Double.compare(a.rate, b.rate));
    
            PriorityQueue<Integer> que = new PriorityQueue<>((a, b) -> b - a);
            int sum = 0;
            for (int i = 0; i < k; i++) {
                que.add(person[i].quality);
                sum += person[i].quality;
            }
            
            double min = sum * person[k - 1].rate;
            for (int i = k; i < n; i++) {
                int max = que.poll();
                que.add(person[i].quality);
                sum = sum - max + person[i].quality;
                min = Math.min(min, sum * person[i].rate);
            }
            
            return min;
        }
        
        class Person{
            public int quality;
            public int wage;
            public double rate;
            public Person(int quality, int wage) {
                this.quality = quality;
                this.wage = wage;
                rate = wage * 1.0 / quality;
            }
        }
    }
    

    Friday, October 19, 2018

    904. Fruit Into Baskets

    904Fruit Into Baskets
    In a row of trees, the i-th tree produces fruit with type tree[i].
    You start at any tree of your choice, then repeatedly perform the following steps:
    1. Add one piece of fruit from this tree to your baskets.  If you cannot, stop.
    2. Move to the next tree to the right of the current tree.  If there is no tree to the right, stop.
    Note that you do not have any choice after the initial choice of starting tree: you must perform step 1, then step 2, then back to step 1, then step 2, and so on until you stop.
    You have two baskets, and each basket can carry any quantity of fruit, but you want each basket to only carry one type of fruit each.
    What is the total amount of fruit you can collect with this procedure?

    Example 1:
    Input: [1,2,1]
    Output: 3
    Explanation: We can collect [1,2,1].
    
    Example 2:
    Input: [0,1,2,2]
    Output: 3
    Explanation: We can collect [1,2,2].
    If we started at the first tree, we would only collect [0, 1].
    
    Example 3:
    Input: [1,2,3,2,2]
    Output: 4
    Explanation: We can collect [2,3,2,2].
    If we started at the first tree, we would only collect [1, 2].
    
    Example 4:
    Input: [3,3,3,1,2,1,1,2,3,3,4]
    Output: 5
    Explanation: We can collect [1,2,1,1,2].
    If we started at the first tree or the eighth tree, we would only collect 4 fruits.
    

    Note:
    1. 1 <= tree.length <= 40000
    2. 0 <= tree[i] < tree.length

    --------------------------
    O(N), 每个树最多被走2遍

    class Solution {
        public int totalFruit(int[] tree) {
            int n = tree.length;
            
            int f1 = 0, f2 = 0, b1 = 0, b2 = 0;
            int max = 0;
            
            for (int i = n - 1; i >= 0; i--) {
                int cand = tree[i];
                if (b1 == 0) {
                    f1 = cand;
                    b1 = 1;
                }else if (cand != f1 && b2 == 0) {
                    f2 = cand;
                    b2 = 1;
                }else if (f1 == cand) {
                    b1++;
                }else if (f2 == cand) {
                    b2++;
                }else {
                    f1 = cand;
                    f2 = tree[i + 1];
                    b1 = 1;
                    b2 = 0; 
                    int tmp = i + 1;
                    while (tree[tmp] == f2) {
                        b2++;
                        tmp++;
                    }
                }
                
                max = Math.max(b1 + b2, max);
            }
            
            return max;
        }
    }
    

    Tuesday, October 16, 2018

    843. Guess the Word

    843Guess the Word
    This problem is an interactive problem new to the LeetCode platform.
    We are given a word list of unique words, each word is 6 letters long, and one word in this list is chosen as secret.
    You may call master.guess(word) to guess a word.  The guessed word should have type string and must be from the original list with 6 lowercase letters.
    This function returns an integer type, representing the number of exact matches (value and position) of your guess to the secret word.  Also, if your guess is not in the given wordlist, it will return -1 instead.
    For each test case, you have 10 guesses to guess the word. At the end of any number of calls, if you have made 10 or less calls to master.guess and at least one of these guesses was the secret, you pass the testcase.
    Besides the example test case below, there will be 5 additional test cases, each with 100 words in the word list.  The letters of each word in those testcases were chosen independently at random from 'a' to 'z', such that every word in the given word lists is unique.
    Example 1:
    Input: secret = "acckzz", wordlist = ["acckzz","ccbazz","eiowzz","abcczz"]
    
    Explanation:
    
    master.guess("aaaaaa") returns -1, because "aaaaaa" is not in wordlist.
    master.guess("acckzz") returns 6, because "acckzz" is secret and has all 6 matches.
    master.guess("ccbazz") returns 3, because "ccbazz" has 3 matches.
    master.guess("eiowzz") returns 2, because "eiowzz" has 2 matches.
    master.guess("abcczz") returns 4, because "abcczz" has 4 matches.
    
    We made 5 calls to master.guess and one of them was the secret, so we pass the test case.
    
    Note:  Any solutions that attempt to circumvent the judge will result in disqualification.
    -------------------------
    扯淡题

    思路是用MiniMax, 主要是求“稳”。有多种实现方式。
    以下方法是,对每一个词,找出与他匹配数量为0的词的个数,然后用数量最小的那个词来猜
    这里的解释不错: http://blog.vrqq.org/archives/363/

    check zero match的原因:
    “Nice Solution. Anyone who doesn't know why checking 0 match instead of 1,2,3...6 matches, please take a look at this comment. The probability of two words with 0 match is (25/26)^6 = 80%. That is to say, for a candidate word, we have 80% chance to see 0 match with the secret word. In this case, we had 80% chance to eliminate the candidate word and its "family" words which have at least 1 match. Additionally, in order to delete a max part of words, we select a candidate who has a big "family" (fewest 0 match).”
    https://leetcode.com/problems/guess-the-word/discuss/133862/Random-Guess-and-Minimax-Guess-with-Comparison
    通常情况下,猜一个词,返回为0的概率胃79% (25/26 ^ 6),这种情况下,wordlist范围缩小的力度也是最小的(因为任意2个词的match为0的几率高达79%)。我们要增大缩小的力度,减小match为0时新生成的list的大小。所以我们选出“找出与他匹配数量为0的词的个数,然后用数量最小的那个词来猜”


    /**
     * // This is the Master's API interface.
     * // You should not implement it, or speculate about its implementation
     * interface Master {
     *     public int guess(String word) {}
     * }
     */
    class Solution {
        public void findSecretWord(String[] wordList, Master master) {
            List<String> list = Arrays.asList(wordList);
            
            while (!list.isEmpty()) {
                Map<String, Integer> zeroMatch = new HashMap<>();
                for (String cand : list) {
                    for (String word : list) {
                        if (match(cand, word) == 0) zeroMatch.put(cand, zeroMatch.getOrDefault(cand, 0) + 1);
                    }
                }    
                
                String guess = list.get(0);
                int min = 110;
                for (Map.Entry<String, Integer> entry : zeroMatch.entrySet()) {
                    if (min > entry.getValue()) {
                        min = entry.getValue();
                        guess = entry.getKey();
                    }
                }
                
                int m = master.guess(guess);
                if (m == 6) return;
                
                List<String> tmp = new ArrayList<>();
                for (String s : list) {
                    if (m == match(s, guess)) {
                        tmp.add(s);    
                    }
                }
                
                list = tmp;
            }
        }
        
        private int match(String a, String b) {
            int m = 0;
            for (int i = 0; i < a.length(); i++) {
                if (a.charAt(i) == b.charAt(i)) m++;
            }
            
            return m;
        }
    }
    

    另一种实现方式,LC官方解答:https://leetcode.com/problems/guess-the-word/solution/

    Wednesday, October 10, 2018

    681. Next Closest Time

    681Next Closest Time
    Given a time represented in the format "HH:MM", form the next closest time by reusing the current digits. There is no limit on how many times a digit can be reused.
    You may assume the given input string is always valid. For example, "01:34", "12:09" are all valid. "1:34", "12:9" are all invalid.
    Example 1:
    Input: "19:34"
    Output: "19:39"
    Explanation: The next closest time choosing from digits 1, 9, 3, 4, is 19:39, which occurs 5 minutes later.  It is not 19:33, because this occurs 23 hours and 59 minutes later.
    
    Example 2:
    Input: "23:59"
    Output: "22:22"
    Explanation: The next closest time choosing from digits 2, 3, 5, 9, is 22:22. It may be assumed that the returned time is next day's time since it is smaller than the input time numerically.
    ----------------------------------
    把所有的时间组合都找出来,去掉不合法的,然后排序。
    另一个方法是模拟始终,一步步往前走
    class Solution {
        public String nextClosestTime(String time) {
            Set<Character> set = new HashSet<>();
            for (int i = 0; i < time.length(); i++) {
                char c = time.charAt(i);
                if (c != ':') set.add(c);
            }
            
            Set<String> rt = new HashSet<>();
            rt.add("");
            for (int i = 0; i < 4; i++) {
                Set<String> tmp = new HashSet<>();
                for (String s : rt) {
                    for (Character c : set) {
                        String ca = s + c;
                        if (i == 3) {
                            if (isValid(ca))
                                tmp.add(s + c);
                        }else {
                            tmp.add(s + c);
                        }
                    }    
                }
                rt = tmp;
                
            }
            
            List<String> fi = new ArrayList<String>(rt);
            Collections.sort(fi);
            String con = time.substring(0, 2) + time.substring(3);
            for (int i = 0; i < fi.size(); i++) {
                if (fi.get(i).equals(con) && i < fi.size() - 1) {
                    return fi.get(i + 1).substring(0, 2) + ":" + fi.get(i + 1).substring(2);
                }
            }
            
            return fi.get(0).substring(0, 2) + ":" + fi.get(0).substring(2);
        }
        
        private boolean isValid(String s) {
            String firstHalf = s.substring(0,2);
            String secondHalf = s.substring(2);
            if (firstHalf.compareTo("23") > 0 || secondHalf.compareTo("59") > 0) return false;
            
            return true;
        }
    }
    

    Tuesday, October 9, 2018

    266. Palindrome Permutation

    266Palindrome Permutation
    Given a string, determine if a permutation of the string could form a palindrome.
    Example 1:
    Input: "code"
    Output: false
    Example 2:
    Input: "aab"
    Output: true
    Example 3:
    Input: "carerac"
    Output: true
    ------------------
    2 pass.
    可以用set来实现single pass,加加减减

    class Solution {
        public boolean canPermutePalindrome(String s) {
            Map<Character, Integer> map = new HashMap<>();
            
            for (int i = 0; i < s.length(); i++) {
                char c = s.charAt(i);
                map.put(c, map.getOrDefault(c, 0) + 1);
            }
            
            boolean odd = false;
            for (Map.Entry<Character, Integer> entry : map.entrySet()) {
                if (entry.getValue() % 2 == 1) {
                    if (odd) return false;
                    odd = true;
                } 
            }
            
            return true;
        }
    }
    

    408. Valid Word Abbreviation

    408Valid Word Abbreviation
    Given a non-empty string s and an abbreviation abbr, return whether the string matches with the given abbreviation.
    A string such as "word" contains only the following valid abbreviations:
    ["word", "1ord", "w1rd", "wo1d", "wor1", "2rd", "w2d", "wo2", "1o1d", "1or1", "w1r1", "1o2", "2r1", "3d", "w3", "4"]
    
    Notice that only the above abbreviations are valid abbreviations of the string "word". Any other string is not a valid abbreviation of "word".
    Note:
    Assume s contains only lowercase letters and abbr contains only lowercase letters and digits.
    Example 1:
    Given s = "internationalization", abbr = "i12iz4n":
    
    Return true.
    
    Example 2:
    Given s = "apple", abbr = "a2e":
    
    Return false.
    --------------------
    分三种情况:
    1. 如果是数字,吃数字
    2. 如果count == 0, 说明大家都指向字符。那就对比字符
    3. 如果不是数字,j += count。 此时i, j都会指向字符或超出范围,两者对比会交给下一次的循环。

    注意几个corner case:
    1. 数字开头为0: a0b
    2. 末尾是数字: ab1。 最后检测长度的时候加上count
    class Solution {
        public boolean validWordAbbreviation(String word, String abbr) {
            int i = 0, j = 0;
            int count = 0;
            
            while (i < abbr.length() && j < word.length()) {
                char c = abbr.charAt(i);
                if (Character.isDigit(c)) {
                    if (count == 0 && c <= '0') return false;
                    count = Character.getNumericValue(c) + count * 10;
                    i++;
                }else if (count == 0) {
                    if (c != word.charAt(j)) return false;
                    i++;
                    j++;
                }else {
                    j += count;
                    count = 0;
                }
            }
            
            return i == abbr.length() && j + count == word.length();
        }
    }
    

    Sunday, October 7, 2018

    658. Find K Closest Elements

    658Find K Closest Elements
    Given a sorted array, two integers k and x, find the k closest elements to x in the array. The result should also be sorted in ascending order. If there is a tie, the smaller elements are always preferred.
    Example 1:
    Input: [1,2,3,4,5], k=4, x=3
    Output: [1,2,3,4]
    
    Example 2:
    Input: [1,2,3,4,5], k=4, x=-1
    Output: [1,2,3,4]
    
    Note:
    1. The value k is positive and will always be smaller than the length of the sorted array.
    2. Length of the given array is positive and will not exceed 104
    3. Absolute value of elements in the array and x will not exceed 104
    ----------------
    Solution #1, O(logN + K)
    class Solution {
        public List<Integer> findClosestElements(int[] arr, int k, int x) {
            int left = findClosest(arr, x);
            int right = left + 1;
            int n = arr.length;
            if (left == -1) {
                right = k;
            }else if (left == n) {
                right = n;
                left = n - k - 1;
            }else {
                while (k > 0) {
                    if (left >= 0 && right < n) {
                        if (x - arr[left] > arr[right] - x) {
                            right++;
                        }else {
                            left--;
                        }
                    }else if (left >= 0) {
                        left--;
                    }else {
                        right++;
                    }
    
                    k--;
                }
            }
            left++;
            right--;
            
            List<Integer> rt = new ArrayList<>();
            for (int i = left; i <= right; i++) {
                rt.add(arr[i]);
            }
            
            return rt;
        }
        
        private int findClosest(int[] arr, int x) {
            int left = 0, right = arr.length - 1;
            
            while (left <= right) {
                int mid = (left + right) / 2;
                if (mid < arr.length - 1 && arr[mid] <= x && arr[mid + 1] > x) return mid;
                if (arr[mid] > x) {
                    right = mid - 1;
                }else {
                    left = mid + 1;
                }
            }
            
            return left;
        }
    }
    

    Solution #2, O(logN + K),ref:https://leetcode.com/problems/find-k-closest-elements/discuss/106419/O(log-n)-Java-1-line-O(log(n)-+-k)-Ruby
    二分查找subarray的起点,用反证法可以证明此算法的正确性
    class Solution {
        public List<Integer> findClosestElements(int[] arr, int k, int x) {
            int left = 0, right = arr.length - k;
            
            while (left < right) {
                int mid = (left + right) / 2;
                if (x - arr[mid] <= arr[mid + k] - x) {
                    right = mid;
                }else {
                    left = mid + 1;
                }
            }
            
            List<Integer> rt = new ArrayList<>();
            for (int i = 0; i < k; i++) {
                rt.add(arr[left + i]);
            }
            
            return rt;
        }
    }
    

    716. Max Stack

    716Max Stack
    Design a max stack that supports push, pop, top, peekMax and popMax.
    1. push(x) -- Push element x onto stack.
    2. pop() -- Remove the element on top of the stack and return it.
    3. top() -- Get the element on the top.
    4. peekMax() -- Retrieve the maximum element in the stack.
    5. popMax() -- Retrieve the maximum element in the stack, and remove it. If you find more than one maximum elements, only remove the top-most one.
    Example 1:
    MaxStack stack = new MaxStack();
    stack.push(5); 
    stack.push(1);
    stack.push(5);
    stack.top(); -> 5
    stack.popMax(); -> 5
    stack.top(); -> 1
    stack.peekMax(); -> 5
    stack.pop(); -> 1
    stack.top(); -> 5
    
    Note:
    1. -1e7 <= x <= 1e7
    2. Number of operations won't exceed 10000.
    3. The last four operations won't be called when stack is empty.
    -------------------------
    Solution #1
    priority queue搞不定,只能treemap。 O(logN)
    class MaxStack {
        
        class Node {
            public Node next;
            public Node pre;
            public int val;
            public Node(int val) {
                this.val = val;
                next = null;
                pre = null;
            }
        }
        
        class DoublyLinkedList {
            private Node head;
            private Node tail;
            
            public DoublyLinkedList() {
                head = new Node(0);
                tail = new Node(0);
                head.next = tail;
                tail.pre = head;
            }
            
            public void addToEnd(Node node) {
                node.pre = tail.pre;
                node.pre.next = node;
                node.next = tail;
                tail.pre = node;
            }
            
            public void remove(Node node) {
                node.pre.next = node.next;
                node.next.pre = node.pre;            
            }
            
            public int getLast() {
                return tail.pre.val;
            }
            
            public int removeLast() {
                int tmp = getLast();
                remove(tail.pre);
                
                return tmp;
            }
        }
    
        private DoublyLinkedList dll;
        private TreeMap<Integer, LinkedList<Node>> map;
        
        /** initialize your data structure here. */
        public MaxStack() {
            dll = new DoublyLinkedList();
            map = new TreeMap<>();
        }
        
        public void push(int x) {
            Node node = new Node(x);
            if (!map.containsKey(x)) {
                map.put(x, new LinkedList<Node>());
            }
            map.get(x).add(node);
            dll.addToEnd(node);
        }
        
        public int pop() {
            int last = dll.removeLast();
            map.get(last).pollLast();
            if (map.get(last).isEmpty()) {
                map.remove(last);
            }
            
            return last;
        }
        
        public int top() {
            return dll.getLast();
        }
        
        public int peekMax() {
            return map.lastKey();
        }
        
        public int popMax() {
            int max = map.lastKey();
            dll.remove(map.get(max).pollLast());
            
            if (map.get(max).isEmpty()) {
                map.remove(max);   
            }
            
            return max;
        }
    }
    
    /**
     * Your MaxStack object will be instantiated and called as such:
     * MaxStack obj = new MaxStack();
     * obj.push(x);
     * int param_2 = obj.pop();
     * int param_3 = obj.top();
     * int param_4 = obj.peekMax();
     * int param_5 = obj.popMax();
     */
    
    Solution #2, 2个stack,ref:https://leetcode.com/problems/max-stack/solution/

    Saturday, October 6, 2018

    329. Longest Increasing Path in a Matrix

    329Longest Increasing Path in a Matrix
    Given an integer matrix, find the length of the longest increasing path.
    From each cell, you can either move to four directions: left, right, up or down. You may NOT move diagonally or move outside of the boundary (i.e. wrap-around is not allowed).
    Example 1:
    Input: nums = 
    [
      [9,9,4],
      [6,6,8],
      [2,1,1]
    ] 
    Output: 4 
    Explanation: The longest increasing path is [1, 2, 6, 9].
    
    Example 2:
    Input: nums = 
    [
      [3,4,5],
      [3,2,6],
      [2,2,1]
    ] 
    Output: 4 
    Explanation: The longest increasing path is [3, 4, 5, 6]. Moving diagonally is not allowed.
    ---------------------------
    Solution #1, typical DFS, 没啥好说的

    class Solution {
        public int longestIncreasingPath(int[][] matrix) {
            if (matrix.length == 0) return 0;
            int m = matrix.length, n = matrix[0].length;
            int[][] inter = new int[m][n];
            int longest = 0;
            
            for (int i = 0; i < m; i++) {
                for (int j = 0; j < n; j++) {
                    longest = Math.max(longest, dfs(matrix, i, j, inter, Integer.MAX_VALUE));
                }
            }
            
            return longest;
        }
        
        private int dfs(int[][] matrix, int row, int col, int[][] inter, int pre) {
            if (row < 0 || row >= matrix.length || col < 0 || col >= matrix[0].length 
               || matrix[row][col] >= pre) return 0;
            
            if (inter[row][col] > 0) return inter[row][col];
            
            int longest = dfs(matrix, row + 1, col, inter, matrix[row][col]);
            longest = Math.max(longest, dfs(matrix, row - 1, col, inter, matrix[row][col]));
            longest = Math.max(longest, dfs(matrix, row, col + 1, inter, matrix[row][col]));
            longest = Math.max(longest, dfs(matrix, row, col - 1, inter, matrix[row][col]));
            inter[row][col] = longest + 1;
            
            return longest + 1;
        }
    }
    

    Solution #2, iterative。也是典型的BFS。ToDo, 有空可以写一下
    找到所有的起点,一起塞进Queue(或List),同时记录步数

    430. Flatten a Multilevel Doubly Linked List

    430Flatten a Multilevel Doubly Linked List
    You are given a doubly linked list which in addition to the next and previous pointers, it could have a child pointer, which may or may not point to a separate doubly linked list. These child lists may have one or more children of their own, and so on, to produce a multilevel data structure, as shown in the example below.
    Flatten the list so that all the nodes appear in a single-level, doubly linked list. You are given the head of the first level of the list.
    Example:
    Input:
     1---2---3---4---5---6--NULL
             |
             7---8---9---10--NULL
                 |
                 11--12--NULL
    
    Output:
    1-2-3-7-8-11-12-9-10-4-5-6-NULL
    
    Explanation for the above example:
    Given the following multilevel doubly linked list:
    We should return the following flattened doubly linked list:
    -------------------------
    Solution #1, recursive
    /*
    // Definition for a Node.
    class Node {
        public int val;
        public Node prev;
        public Node next;
        public Node child;
    
        public Node() {}
    
        public Node(int _val,Node _prev,Node _next,Node _child) {
            val = _val;
            prev = _prev;
            next = _next;
            child = _child;
        }
    };
    */
    class Solution {
        public Node flatten(Node head) {
            if (head == null) return null;
            Node cp = head;
            rec(cp);
            
            return head;
        }
        
        private Node rec(Node head) {
            if (head.next == null && head.child == null) return head;
            
            if (head.child == null) {
                return rec(head.next);
            }
            
            Node savedNext = head.next;
            head.next = head.child;
            head.child.prev = head;
    
            Node last = rec(head.child);
            head.child = null;
            last.next = savedNext;
            if (savedNext != null) {
                savedNext.prev = last;
            }
            savedNext = last;
            return rec(savedNext);
        }
    }
    

    Solution #2, iterative. 如果有child,就把child整个list并回主list(暂时先放过child的child),然后再从child开始走。每次都减少一层
    O(n), 因为每个node最多被走了2次
    /*
    // Definition for a Node.
    class Node {
        public int val;
        public Node prev;
        public Node next;
        public Node child;
    
        public Node() {}
    
        public Node(int _val,Node _prev,Node _next,Node _child) {
            val = _val;
            prev = _prev;
            next = _next;
            child = _child;
        }
    };
    */
    class Solution {
        public Node flatten(Node head) {
            Node itr = head;
            
            while (itr != null) {
                if (itr.child != null) {
                    Node savedNext = itr.next;
                    itr.next = itr.child;
                    itr.child.prev = itr;
                    itr.child = null;
                    
                    Node itrInner = itr.next;
                    while (itrInner.next != null) {
                        itrInner = itrInner.next;
                    }
                    
                    itrInner.next = savedNext;
                    if (savedNext != null) savedNext.prev = itrInner;
                }
                
                itr = itr.next;
            }
            
            return head;
        }
    }