Sunday, September 30, 2018

688. Knight Probability in Chessboard

688Knight Probability in Chessboard
On an NxN chessboard, a knight starts at the r-th row and c-th column and attempts to make exactly K moves. The rows and columns are 0 indexed, so the top-left square is (0, 0), and the bottom-right square is (N-1, N-1).
A chess knight has 8 possible moves it can make, as illustrated below. Each move is two squares in a cardinal direction, then one square in an orthogonal direction.

Each time the knight is to move, it chooses one of eight possible moves uniformly at random (even if the piece would go off the chessboard) and moves there.
The knight continues moving until it has made exactly K moves or has moved off the chessboard. Return the probability that the knight remains on the board after it has stopped moving.
Example:
Input: 3, 2, 0, 0
Output: 0.0625
Explanation: There are two moves (to (1,2), (2,1)) that will keep the knight on the board.
From each of those positions, there are also two moves that will keep the knight on the board.
The total probability the knight stays on the board is 0.0625.
Note:




  • N will be between 1 and 25.
  • K will be between 0 and 100.
  • The knight always initially starts on the board.

  • -----------------------
    典型的DP,按套路一路推理

    Solution #1 超时
    class Solution {
        
        public double knightProbability(int N, int K, int r, int c) {
            int total = 1;
            for (int i = 0; i < K; i++) {
                total *= 8;
            }
           
            return dfs(N, K, r, c) / total;
        }
        
        public double dfs(int N, int K, int r, int c) {
            if (r < 0 || r >= N || c < 0 || c >= N) {
                return 0;
            }
    
            if (K == 0) {
                return 1;
            }
            
            return  dfs(N, K - 1, r - 2, c - 1) +
                + dfs(N, K - 1, r - 2, c + 1)
                + dfs(N, K - 1, r - 1, c - 2)
                + dfs(N, K - 1, r - 1, c + 2)
                + dfs(N, K - 1, r + 1, c - 2)
                + dfs(N, K - 1, r + 1, c + 2)
                + dfs(N, K - 1, r + 2, c - 1)
                + dfs(N, K - 1, r + 2, c + 1);
        }
    }
    

    Solution #2 Memoization
    O(N^2 * K) 时间
    class Solution {
        private int[] dr = {-2, -2, -1, -1, 1, 1, 2, 2};
        private int[] dc = {-1, 1, -2, 2, -2, 2, -1, 1};
             
        public double knightProbability(int N, int K, int r, int c) {
            
            double[][][] dp = new double[N][N][K];
            for (int i = 0; i < N; i++) {
                for (int j = 0; j < N; j++)
                    Arrays.fill(dp[i][j], -1);
            }
            double a = dfs(N, K, r, c, dp);
            return  a / Math.pow(8, K);
        }
        
        public double dfs(int N, int K, int r, int c, double[][][] dp) {
            if (r < 0 || r >= N || c < 0 || c >= N) {
                return 0;
            }
            
            if (K == 0) {
                return 1;
            }
            if (dp[r][c][K - 1] != -1) return dp[r][c][K - 1];
    
            double total = 0;
            for (int i = 0; i < 8; i++) {
                total += dfs(N, K - 1, r + dr[i], c + dc[i], dp);
            }
            
            dp[r][c][K - 1] = total;
            return total;
        }
    }
    

    Solution #3 DP, 思路同上。
    因为[k]仅依赖于[k - 1], 所以用2个2维数组可以交替使用,不用3维
    O(N^2 * K) 时间,O(N^2) 空间
    class Solution {
        private int[] dr = {-2, -2, -1, -1, 1, 1, 2, 2};
        private int[] dc = {-1, 1, -2, 2, -2, 2, -1, 1};
             
        public double knightProbability(int N, int K, int r, int c) {
            
            double[][] dp1 = new double[N][N];
            double[][] dp2 = new double[N][N];    
            dp1[r][c] = 1;
            
            for (int k = 0; k < K; k++) {
                for (int i = 0; i < N; i++) {
                    for (int j = 0; j < N; j++) {
                        if (dp1[i][j] > 0) {
                            for (int d = 0; d < 8; d++) {
                                helper(N, i, j, i + dr[d], j + dc[d], dp1, dp2);   
                            }                
                            dp1[i][j] = 0;
                        }
                    }
                }
                double[][] tmp = dp1;
                dp1 = dp2;
                dp2 = tmp;
            }
            
            double total = 0;
            for (int i = 0; i < N; i++) {
                for (int j = 0; j < N; j++) total += dp1[i][j];
            }
            
            return  total / Math.pow(8, K);
        }
        
        public void helper(int N, int r1, int c1, int r2, int c2, double[][] dp1, double[][] dp2) {
            
            if (r2 < 0 || r2 >= N || c2 < 0 || c2 >= N) {
                return ;
            }
            dp2[r2][c2] += dp1[r1][c1];
        }
    }
    

    630. Course Schedule III

    630Course Schedule III
    There are n different online courses numbered from 1 to n. Each course has some duration(course length) t and closed on dthday. A course should be taken continuously for t days and must be finished before or on the dth day. You will start at the 1st day.
    Given n online courses represented by pairs (t,d), your task is to find the maximal number of courses that can be taken.
    Example:
    Input: [[100, 200], [200, 1300], [1000, 1250], [2000, 3200]]
    Output: 3
    Explanation: 
    There're totally 4 courses, but you can take 3 courses at most:
    First, take the 1st course, it costs 100 days so you will finish it on the 100th day, and ready to take the next course on the 101st day.
    Second, take the 3rd course, it costs 1000 days so you will finish it on the 1100th day, and ready to take the next course on the 1101st day. 
    Third, take the 2nd course, it costs 200 days so you will finish it on the 1300th day. 
    The 4th course cannot be taken now, since you will finish it on the 3300th day, which exceeds the closed date.
    
    Note:
    1. The integer 1 <= d, t, n <= 10,000.
    2. You can't take two courses simultaneously.
    ---------------------------------
    ToDo
    class Solution {
        public int scheduleCourse(int[][] courses) {
            Arrays.sort(courses, (a, b) -> a[1] - b[1]);
            
            PriorityQueue<Integer> que = new PriorityQueue<>((a, b) -> b - a);
            int now = 0;
            for (int i = 0; i < courses.length; i++) {
                que.add(courses[i][0]);
                now += courses[i][0];
                if (now > courses[i][1]) {
                    int ou = que.poll();
                    now -= ou;
                }
            }
            
            return que.size();
        }
    }
    

    Thursday, September 27, 2018

    360. Sort Transformed Array

    360Sort Transformed Array
    Given a sorted array of integers nums and integer values ab and c. Apply a quadratic function of the form f(x) = ax2 + bx + c to each element x in the array.
    The returned array must be in sorted order.
    Expected time complexity: O(n)
    Example 1:
    Input: nums = [-4,-2,2,4], a = 1, b = 3, c = 5
    Output: [3,9,15,33]
    
    Example 2:
    Input: nums = [-4,-2,2,4], a = -1, b = 3, c = 5
    Output: [-23,-5,1,7]
    ---------------------
    一元二次方程
    咋看之下有4种情况 a > 0, a < 0, a == 0 && b > 0, a ==0 && b < 0, 其实代码一整合就只有2种:a >= 0和a < 0

    class Solution {
        public int[] sortTransformedArray(int[] nums, int a, int b, int c) {
            int n = nums.length;
            int[] rt = new int[n];
            int left = 0, right = n - 1;
            int itr = a >= 0 ? n - 1 : 0;
            
            while (left <= right) {
                if (a >= 0) {
                    if (f(nums[left], a, b, c) > f(nums[right], a, b, c)) {
                        rt[itr] = f(nums[left], a, b, c);
                        left++;
                    }else {
                         rt[itr] = f(nums[right], a, b, c);
                        right--;
                    }          
                    itr--;
                }else {
                    if (f(nums[left], a, b, c) < f(nums[right], a, b, c)) {
                        rt[itr] = f(nums[left], a, b, c);
                        left++;
                    }else {
                         rt[itr] = f(nums[right], a, b, c);
                        right--;
                    }          
                    itr++;
                }
            }
            
            return rt;
        }
        
        private int f(int x, int a, int b, int c) {
            return a * x * x + b * x + c;
        }
    }
    

    Monday, September 24, 2018

    463. Island Perimeter

    463Island Perimeter
    You are given a map in form of a two-dimensional integer grid where 1 represents land and 0 represents water. Grid cells are connected horizontally/vertically (not diagonally). The grid is completely surrounded by water, and there is exactly one island (i.e., one or more connected land cells). The island doesn't have "lakes" (water inside that isn't connected to the water around the island). One cell is a square with side length 1. The grid is rectangular, width and height don't exceed 100. Determine the perimeter of the island.
    Example:
    [[0,1,0,0],
     [1,1,1,0],
     [0,1,0,0],
     [1,1,0,0]]
    
    Answer: 16
    Explanation: The perimeter is the 16 yellow stripes in the image below:
    
    ---------------------
    数1周边是0的个数总和
    class Solution {
        public int islandPerimeter(int[][] grid) {
            int m = grid.length;
            int n = grid[0].length;
            
            int perimeter = 0;
            for (int i = 0; i < m; i++) {
                for (int j = 0; j < n; j++) {
                    if (grid[i][j] == 1) {
                        perimeter += countEdge(grid, i, j);
                    }
                }
            }
            
            return perimeter;
        }
        
        private int countEdge(int[][] grid, int row, int col) {
            return helper(grid, row + 1, col) + helper(grid, row, col + 1) 
                + helper(grid, row, col - 1) + helper(grid, row - 1, col);
        }
        
        private int helper(int[][] grid, int row, int col) {
            if (row >= 0 && row < grid.length && col >= 0 && col < grid[0].length && grid[row][col] == 0) {
                return 1;
            }
            if (row == -1 || row == grid.length || col == -1 || col == grid[0].length) {
                return 1;
            }
            return 0;
        }
    }
    
    另一种方法是计1的个数为j,对每一个1都找出它周边是1的个数总和k,最终4 * j - 2 * k

    824. Goat Latin

    824Goat Latin
    A sentence S is given, composed of words separated by spaces. Each word consists of lowercase and uppercase letters only.
    We would like to convert the sentence to "Goat Latin" (a made-up language similar to Pig Latin.)
    The rules of Goat Latin are as follows:
    • If a word begins with a vowel (a, e, i, o, or u), append "ma" to the end of the word.
      For example, the word 'apple' becomes 'applema'.
       
    • If a word begins with a consonant (i.e. not a vowel), remove the first letter and append it to the end, then add "ma".
      For example, the word "goat" becomes "oatgma".
       
    • Add one letter 'a' to the end of each word per its word index in the sentence, starting with 1.
      For example, the first word gets "a" added to the end, the second word gets "aa" added to the end and so on.
    Return the final sentence representing the conversion from S to Goat Latin. 
    ----------------
    class Solution {
        public String toGoatLatin(String S) {
            Set vowels = getVowels();
            String[] words = S.split("\\s+");
            String a = "a";
            
            for (int i = 0; i < words.length; i++) {
                String word = words[i];
                if (!vowels.contains(word.charAt(0))) {
                    words[i] = word.substring(1) + word.charAt(0);
                }
                words[i] += "ma" + a;
                a += "a";
            }
            
            return String.join(" ", words);
        }
        
        private Set getVowels() {
            Set s = new HashSet<>();
            s.add('a');
            s.add('e');
            s.add('i');
            s.add('o');
            s.add('u');
            s.add('A');
            s.add('E');
            s.add('I');
            s.add('O');
            s.add('U');
            
            return s;
        }
    }
    

    Sunday, September 23, 2018

    395. Longest Substring with At Least K Repeating Characters

    395Longest Substring with At Least K Repeating Characters
    Find the length of the longest substring T of a given string (consists of lowercase letters only) such that every character in T appears no less than k times.
    Example 1:
    Input:
    s = "aaabb", k = 3
    
    Output:
    3
    
    The longest substring is "aaa", as 'a' is repeated 3 times.
    
    Example 2:
    Input:
    s = "ababbc", k = 2
    
    Output:
    5
    
    The longest substring is "ababb", as 'a' is repeated 2 times and 'b' is repeated 3 times.
    -------------------------
    递归,找出当前String里数量小于k的字符的index,以这些index作为分界点,对substring进行相同操作。

    O(n), n是String的长度。因为最多只有26个字符,每一层递归都会去除一个字符

    class Solution {
        public int longestSubstring(String s, int k) {
            Map<Character, Integer> map = getMap(s);
            Stack<Integer> st = new Stack<>();
            st.push(-1);
            
            for (int i = 0; i < s.length(); i++) {
                if (map.get(s.charAt(i)) < k) {
                    st.push(i);
                }
            }
            
            if (st.size() == 1) return s.length();
            
            int len = 0;
            int end = s.length();
            
            while (!st.isEmpty()) {
                int start = st.pop();
                len = Math.max(len, longestSubstring(s.substring(start + 1, end), k));
                end = start;
            }
            
            return len;
        }
        
        private Map<Character, Integer> getMap(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);  
            }
            
            return map;
        }
    }
    

    Saturday, September 22, 2018

    387. First Unique Character in a String

    387First Unique Character in a String
    Given a string, find the first non-repeating character in it and return it's index. If it doesn't exist, return -1.
    Examples:
    s = "leetcode"
    return 0.
    
    s = "loveleetcode",
    return 2.
    
    Note: You may assume the string contain only lowercase letters.

    --------------------
    2 passes
    class Solution {
        public int firstUniqChar(String s) {
            Map<Character, Integer> map = new HashMap<>();
            
            for (int i = 0; i < s.length(); i++) {
                map.put(s.charAt(i), map.getOrDefault(s.charAt(i), 0) + 1);
            }
            
            for (int i = 0; i < s.length(); i++) {
                if (map.get(s.charAt(i)) == 1) return i;
            }
            
            return -1;
        }
    }
    

    Just for grins, 1 pass的方法可以考虑用set +LinkedHashMap, LinkedHashMap default的特性是保证key insertion order, Java doc

    489. Robot Room Cleaner

    489Robot Room Cleaner
    Given a robot cleaner in a room modeled as a grid.
    Each cell in the grid can be empty or blocked.
    The robot cleaner with 4 given APIs can move forward, turn left or turn right. Each turn it made is 90 degrees.
    When it tries to move into a blocked cell, its bumper sensor detects the obstacle and it stays on the current cell.
    Design an algorithm to clean the entire room using only the 4 given APIs shown below.
    interface Robot {
      // returns true if next cell is open and robot moves into the cell.
      // returns false if next cell is obstacle and robot stays on the current cell.
      boolean move();
    
      // Robot will stay on the same cell after calling turnLeft/turnRight.
      // Each turn will be 90 degrees.
      void turnLeft();
      void turnRight();
    
      // Clean the current cell.
      void clean();
    }
    
    Example:
    Input:
    room = [
      [1,1,1,1,1,0,1,1],
      [1,1,1,1,1,0,1,1],
      [1,0,1,1,1,1,1,1],
      [0,0,0,1,0,0,0,0],
      [1,1,1,1,1,1,1,1]
    ],
    row = 1,
    col = 3
    
    Explanation:
    All grids in the room are marked by either 0 or 1.
    0 means the cell is blocked, while 1 means the cell is accessible.
    The robot initially starts at the position of row=1, col=3.
    From the top left corner, its position is one row below and three columns right.
    
    Notes:
    1. The input is only given to initialize the room and the robot's position internally. You must solve this problem "blindfolded". In other words, you must control the robot using only the mentioned 4 APIs, without knowing the room layout and the initial robot's position.
    2. The robot's initial position will always be in an accessible cell.
    3. The initial direction of the robot will be facing up.
    4. All accessible cells are connected, which means the all cells marked as 1 will be accessible by the robot.
    5. Assume all four edges of the grid are all surrounded by wall.
    ---------------------
    DFS + backtracking. 注意每次dfs都要传入一个当前的方向
    /**
     * // This is the robot's control interface.
     * // You should not implement it, or speculate about its implementation
     * interface Robot {
     *     // Returns true if the cell in front is open and robot moves into the cell.
     *     // Returns false if the cell in front is blocked and robot stays in the current cell.
     *     public boolean move();
     *
     *     // Robot will stay in the same cell after calling turnLeft/turnRight.
     *     // Each turn will be 90 degrees.
     *     public void turnLeft();
     *     public void turnRight();
     *
     *     // Clean the current cell.
     *     public void clean();
     * }
     */
    class Solution {
        private int[][] dirs = {{-1,0}, {0,1}, {1,0}, {0,-1}};
        public void cleanRoom(Robot robot) {
            Set<String> visited = new HashSet<>();
            dfs(robot, visited, 0, 0, 0);
        }
        
        private void dfs(Robot robot, Set<String> visited, int row, int col, int curDir) {
            String key =  row + ":" + col;
            if (visited.contains(key)) return;
            
            visited.add(key);
            robot.clean();
            for (int i = 0; i < 4; i++) {
                if (robot.move()) {
                    
                    dfs(robot, visited, row + dirs[curDir][0], col + dirs[curDir][1], curDir);
                    robot.turnLeft();
                    robot.turnLeft();
                    robot.move();
                    robot.turnLeft();
                    robot.turnLeft();
                } 
                
                robot.turnRight();
                curDir = (curDir + 1) % 4;  
            }
            
        }
    }
    

    Friday, September 21, 2018

    304. 303. Range Sum Query 2D - Immutable, Range Sum Query - Immutable

    304Range Sum Query 2D - Immutable
    Given a 2D matrix matrix, find the sum of the elements inside the rectangle defined by its upper left corner (row1, col1) and lower right corner (row2, col2).
    Range Sum Query 2D
    The above rectangle (with the red border) is defined by (row1, col1) = (2, 1) and (row2, col2) = (4, 3), which contains sum = 8.
    Example:
    Given matrix = [
      [3, 0, 1, 4, 2],
      [5, 6, 3, 2, 1],
      [1, 2, 0, 1, 5],
      [4, 1, 0, 1, 7],
      [1, 0, 3, 0, 5]
    ]
    
    sumRegion(2, 1, 4, 3) -> 8
    sumRegion(1, 1, 2, 2) -> 11
    sumRegion(1, 2, 2, 4) -> 12
    
    Note:
    1. You may assume that the matrix does not change.
    2. There are many calls to sumRegion function.
    3. You may assume that row1 ≤ row2 and col1 ≤ col2.
    -----------------------
    二维累计和,多种方法,binary indexed tree, segment tree都可以解。
    不过此题要求是immutable, 不用更新,所以最简单的2d array是最优的

    class NumMatrix {
    
        private int[][] rangeSum;
        public NumMatrix(int[][] matrix) {
            preProcess(matrix);
        }
        
        private void preProcess(int[][] matrix) {
            int m = matrix.length;
            if (m == 0) return;
            
            int n = matrix[0].length;
            rangeSum = new int[m][n];
            
            for (int i = 0; i < m; i++) {
                int rowSum = 0;
                for (int j = 0; j < n; j++) {
                    rowSum += matrix[i][j];
                    if (i == 0) {
                        rangeSum[i][j] = rowSum;
                    }else {
                        rangeSum[i][j] = rowSum + rangeSum[i - 1][j];
                    }
                }
            }
        }
        
        public int sumRegion(int row1, int col1, int row2, int col2) {
            if (row1 == 0 && col1 == 0) {
                return rangeSum[row2][col2];
            }else if (row1 == 0) {
                return rangeSum[row2][col2] - rangeSum[row2][col1 - 1];
            }else if (col1 == 0) {
                return rangeSum[row2][col2] - rangeSum[row1 - 1][col2];
            }
            
            return rangeSum[row2][col2] + rangeSum[row1 - 1][col1 - 1] 
                - rangeSum[row2][col1 - 1] - rangeSum[row1 - 1][col2];
        }
    }
    
    /**
     * Your NumMatrix object will be instantiated and called as such:
     * NumMatrix obj = new NumMatrix(matrix);
     * int param_1 = obj.sumRegion(row1,col1,row2,col2);
     */
    

    303Range Sum Query - Immutable
    Given an integer array nums, find the sum of the elements between indices i and j (i ≤ j), inclusive.
    Example:
    Given nums = [-2, 0, 3, -5, 2, -1]
    
    sumRange(0, 2) -> 1
    sumRange(2, 5) -> -1
    sumRange(0, 5) -> -3
    
    Note:
    1. You may assume that the array does not change.
    2. There are many calls to sumRange function.
    ------------
    class NumArray {
    
        private int[] rangeSum;
        public NumArray(int[] nums) {
            int n = nums.length;
            rangeSum = new int[n + 1];
            
            for (int i = 0; i < nums.length; i++) {
                rangeSum[i + 1] += rangeSum[i] + nums[i];
            }
        }
        
        public int sumRange(int i, int j) {
            return rangeSum[j + 1] - rangeSum[i];
        }
    }
    
    /**
     * Your NumArray object will be instantiated and called as such:
     * NumArray obj = new NumArray(nums);
     * int param_1 = obj.sumRange(i,j);
     */
    

    Thursday, September 20, 2018

    567. Permutation in String

    567Permutation in String
    Given two strings s1 and s2, write a function to return true if s2 contains the permutation of s1. In other words, one of the first string's permutations is the substring of the second string.
    Example 1:
    Input:s1 = "ab" s2 = "eidbaooo"
    Output:True
    Explanation: s2 contains one permutation of s1 ("ba").
    
    Example 2:
    Input:s1= "ab" s2 = "eidboaoo"
    Output: False
    
    Note:
    1. The input strings only contain lower case letters.
    2. The length of both given strings is in range [1, 10,000].
    --------------------
    Sliding window

    class Solution {
        public boolean checkInclusion(String s1, String s2) {
            Map<Character, Integer> map = getMap(s1);
            int count = s1.length();
            int start = -1;
            
            for (int i = 0; i < s2.length(); i++) {
                char c = s2.charAt(i);
                if (map.containsKey(c) && map.get(c) > 0) {
                    if (count == s1.length()) start = i;
    
                    map.put(c, map.get(c) - 1);
                    count--;
                    if (count == 0) return true;
    
                }else if ((map.containsKey(c) && map.get(c) == 0) || !map.containsKey(c)) {
                    while (count < s1.length() && s2.charAt(start) != c) {
                        count++;
                        map.put(s2.charAt(start), map.get(s2.charAt(start)) + 1);
                        start++;
                    }
    
                    start++;
                }
            }
            
            return false;
        }
        
        private Map<Character, Integer> getMap(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);
            }
            
            return map;
        }
        
    }
    

    126. Word Ladder II

    126Word Ladder II
    Given two words (beginWord and endWord), and a dictionary's word list, find all shortest transformation sequence(s) from beginWord to endWord, such that:
    1. Only one letter can be changed at a time
    2. Each transformed word must exist in the word list. Note that beginWord is not a transformed word.
    Note:
    • Return an empty list if there is no such transformation sequence.
    • All words have the same length.
    • All words contain only lowercase alphabetic characters.
    • You may assume no duplicates in the word list.
    • You may assume beginWord and endWord are non-empty and are not the same.
    Example 1:
    Input:
    beginWord = "hit",
    endWord = "cog",
    wordList = ["hot","dot","dog","lot","log","cog"]
    
    Output:
    [
      ["hit","hot","dot","dog","cog"],
      ["hit","hot","lot","log","cog"]
    ]
    
    Example 2:
    Input:
    beginWord = "hit"
    endWord = "cog"
    wordList = ["hot","dot","dog","lot","log"]
    
    Output: []
    
    Explanation: The endWord "cog" is not in wordList, therefore no possible transformation.
    -----------------
    在I上做了一些修改,每个Pair都会记录当前走过的点
    class Solution {
        public List<List<String>> findLadders(String beginWord, String endWord, List<String> wordList) {
            int n = beginWord.length();
            Map<String, Integer> dic = getDic(wordList);
            dic.put(beginWord, 1);
            Queue<Pair> que = new LinkedList<>();
            List<String> ll = new ArrayList<>();
            ll.add(beginWord);
            que.add(new Pair(1, beginWord, ll));
            List<List<String>> rt = new ArrayList<>();
            
            while (!que.isEmpty()) {
                Pair p = que.poll();
                String cur = p.s;
                
                if (!rt.isEmpty() && p.level > rt.get(0).size()) {
                    return rt;
                }
                if (cur.equals(endWord)) {
                    rt.add(p.list);
                    continue;
                }
                
                for (int i = 0; i < n; i++) {
                    char c = cur.charAt(i);
                    for (char nextC = 'a'; nextC <= 'z'; nextC++) {
                        String next = cur.substring(0, i) + nextC + cur.substring(i + 1);
                        
                        // Note: ">=", as we want to output all possible paths.
                        if (dic.containsKey(next) && dic.get(next) >= p.level + 1) { 
                            List<String> l = new ArrayList<>(p.list);
                            l.add(next);
                            que.add(new Pair(p.level + 1, next, l));
                            dic.put(next, p.level + 1);
                        }
                    }
                }
            }
            
            return rt;
        }
        
        private Map<String, Integer> getDic(List<String> wordList) {
            Map<String, Integer> dic = new HashMap<>();
            
            for (String s : wordList) {
                dic.put(s, Integer.MAX_VALUE);
            }
            
            return dic;
        }
        
        class Pair{
            public int level;
            public String s;
            public List<String> list;
            public Pair(int level, String s, List<String> list) {
                this.level = level;
                this.s = s;
                this.list = list;
            }
        }
    }
    

    Wednesday, September 19, 2018

    498. Diagonal Traverse

    498Diagonal Traverse
    Given a matrix of M x N elements (M rows, N columns), return all elements of the matrix in diagonal order as shown in the below image.
    Example:
    Input:
    [
     [ 1, 2, 3 ],
     [ 4, 5, 6 ],
     [ 7, 8, 9 ]
    ]
    Output:  [1,2,4,7,5,3,6,8,9]
    Explanation:
    
    
    Note:
    1. The total number of elements of the given matrix will not exceed 10,000.
    --------------------------
    分(上,右)和(下,左)2种情况,长方形的4个顶点可能需要特殊处理
    class Solution {
        public int[] findDiagonalOrder(int[][] matrix) {
            if (matrix.length == 0) return new int[0];
            
            int m = matrix.length, n = matrix[0].length;
            int[] rt = new int[m * n];
            int row = 0, col = 0, i = 0;
            int up = 1;
            
            while (row != m - 1 || col != n - 1) {
                rt[i] = matrix[row][col];
                
                if ((row == 0 || col == n - 1) && up == 1) {
                    if (col != n - 1 && row == 0) {
                        col++;
                    }else {
                        row++;
                    }
                    
                    up = -1;
                }else if ((row == m - 1 || col == 0) && up == -1) {
                    if (row == m - 1) {
                        col++;
                    }else {
                        row++;
                    }
                    
                    up = 1;
                } else {
                    row -= up;
                    col += up;
                }
                i++;
            }
            
            rt[m * n - 1] = matrix[m - 1][n - 1];
            return rt;
        }
    }
    

    Monday, September 17, 2018

    432. All O`one Data Structure

    432All O`one Data Structure
    Implement a data structure supporting the following operations:
    1. Inc(Key) - Inserts a new key with value 1. Or increments an existing key by 1. Key is guaranteed to be a non-empty string.
    2. Dec(Key) - If Key's value is 1, remove it from the data structure. Otherwise decrements an existing key by 1. If the key does not exist, this function does nothing. Key is guaranteed to be a non-empty string.
    3. GetMaxKey() - Returns one of the keys with maximal value. If no element exists, return an empty string "".
    4. GetMinKey() - Returns one of the keys with minimal value. If no element exists, return an empty string "".
    Challenge: Perform all these in O(1) time complexity.
    ----------------------------
    用doubly linked list, value相同的string都放在一个node里面

    class AllOne {
    
        class Node{
            public Node pre;
            public Node next;
            public int val;
            public boolean isDummy;
            public Set<String> set;
            public Node(int val) {
                this.val = val;
                set = new HashSet<>();
                isDummy = false;
            }
        }
        
        private Node min;
        private Node max;
        private Map<String, Integer> map;
        private Map<Integer, Node> iToN;
        
        /** Initialize your data structure here. */
        public AllOne() {
            min = new Node(0);
            max = new Node(0);
            min.next = max;
            max.pre = min;
            min.isDummy = true;
            max.isDummy = true;
            
            map = new HashMap<>();
            iToN = new HashMap<>();
        }
        
        /** Inserts a new key <Key> with value 1. Or increments an existing key by 1. */
        public void inc(String key) {
            if (!map.containsKey(key)) {
                addOne(key);
            }else {
                int val = map.get(key);
                map.put(key, val + 1);
                
                addToNewNode(key, val + 1);
                removeKey(key, val);
            }
        }
        
        private void addToNewNode(String key, int val) {
            if (iToN.containsKey(val)) {
                iToN.get(val).set.add(key);
            }else {
                Node node = new Node(val);
                node.set.add(key);
                iToN.put(val, node);
                
                Node pre = iToN.get(val - 1);
                insertAfter(pre, node);
            }
        }
        
        private void insertAfter(Node cur, Node node) {
            Node next = cur.next;
            cur.next = node;
            node.pre = cur;
            node.next = next;
            next.pre = node;
        }
        
        private void removeKey(String key, int val) {
            Node node = iToN.get(val);
            node.set.remove(key);
            if (node.set.isEmpty()) {
                Node pre = node.pre;
                Node next = node.next;
                pre.next = next;
                next.pre = pre;
                
                iToN.remove(val);
            }
        }
        
        private void addOne(String key) {
            map.put(key, 1);
            
            if (iToN.containsKey(1)) {
                iToN.get(1).set.add(key);
            }else {
                Node node = new Node(1);
                node.set.add(key);
                iToN.put(1, node);
    
                insertAfter(min, node);
            }
        }
        
        /** Decrements an existing key by 1. If Key's value is 1, remove it from the data structure. */
        public void dec(String key) {
            if (!map.containsKey(key)) return;
            
            int val = map.get(key);
            if (val == 1) {
                map.remove(key);
                removeKey(key, 1);
            }else {
                if (iToN.containsKey(val - 1)) {
                    iToN.get(val - 1).set.add(key);
                }else {
                    Node node = new Node(val - 1);
                    node.set.add(key);
                    iToN.put(val - 1, node);
                    Node pre = iToN.get(val).pre;
                    
                    insertAfter(pre, node);
                }
                
                removeKey(key, val);
                map.put(key, val - 1);
            }
        }
        
        /** Returns one of the keys with maximal value. */
        public String getMaxKey() {
            if (max.pre.isDummy) return "";
            Iterator<String> itr = max.pre.set.iterator();
            return itr.next();
        }
        
        /** Returns one of the keys with Minimal value. */
        public String getMinKey() {
            if (min.next.isDummy) return "";
            Iterator<String> itr = min.next.set.iterator();
            return itr.next();
        }
    }
    
    /**
     * Your AllOne object will be instantiated and called as such:
     * AllOne obj = new AllOne();
     * obj.inc(key);
     * obj.dec(key);
     * String param_3 = obj.getMaxKey();
     * String param_4 = obj.getMinKey();
     */
    

    Friday, September 14, 2018

    505. The Maze II

    505The Maze II
    There is a ball in a maze with empty spaces and walls. The ball can go through empty spaces by rolling updownleft or right, but it won't stop rolling until hitting a wall. When the ball stops, it could choose the next direction.
    Given the ball's start position, the destination and the maze, find the shortest distance for the ball to stop at the destination. The distance is defined by the number of empty spaces traveled by the ball from the start position (excluded) to the destination (included). If the ball cannot stop at the destination, return -1.
    The maze is represented by a binary 2D array. 1 means the wall and 0 means the empty space. You may assume that the borders of the maze are all walls. The start and destination coordinates are represented by row and column indexes.
    Example 1
    Input 1: a maze represented by a 2D array
    
    0 0 1 0 0
    0 0 0 0 0
    0 0 0 1 0
    1 1 0 1 1
    0 0 0 0 0
    
    Input 2: start coordinate (rowStart, colStart) = (0, 4)
    Input 3: destination coordinate (rowDest, colDest) = (4, 4)
    
    Output: 12
    Explanation: One shortest way is : left -> down -> left -> down -> right -> down -> right.
                 The total distance is 1 + 1 + 3 + 1 + 2 + 2 + 2 = 12.
    
    
    Example 2
    Input 1: a maze represented by a 2D array
    
    0 0 1 0 0
    0 0 0 0 0
    0 0 0 1 0
    1 1 0 1 1
    0 0 0 0 0
    
    Input 2: start coordinate (rowStart, colStart) = (0, 4)
    Input 3: destination coordinate (rowDest, colDest) = (3, 2)
    
    Output: -1
    Explanation: There is no way for the ball to stop at the destination.
    
    
    Note:
    1. There is only one ball and one destination in the maze.
    2. Both the ball and the destination exist on an empty space, and they will not be at the same position initially.
    3. The given maze does not contain border (like the red rectangle in the example pictures), but you could assume the border of the maze are all walls.
    4. The maze contains at least 2 empty spaces, and both the width and height of the maze won't exceed 100.
    ------------------
    这题其实是一个weighted grahp,vertex是每一个靠墙的点,edge weight是每个vertex之间的距离。所以搜索的方法有dfs,bfs跟dijkstra。复杂度为 (|E| + |V|) * K,K为在每个vertex上的花费

    Solution #1, 在Maze I的基础上进行的修改。
    1. 因为每一步走的距离都是不等的,所以得保持另一个2d array来记录当前坐标的距离
    2. 可以不用预先把记录距离的2d array填满,但是那种算法oj内存溢出,很奇怪
    Worst complexity是O((m * n)^2),

    class Solution {
        public int shortestDistance(int[][] maze, int[] start, int[] destination) {
            Queue<int[]> que = new LinkedList<>();
            que.add(start);
            int[][] dirs = {{1,0},{-1,0},{0,1},{0,-1}};
            int[][] dis = new int[maze.length][maze[0].length];
            for (int[] row: dis)
                Arrays.fill(row, Integer.MAX_VALUE);
            dis[start[0]][start[1]] = 0;
            
            while (!que.isEmpty()) {
                int[] pos = que.poll();
                
                for (int[] dir : dirs) {
                    int i = 0;
                    while (pos[0] + dir[0] * i >= 0 && pos[0] + dir[0] * i < maze.length
                          && pos[1] + dir[1] * i >= 0 && pos[1] + dir[1] * i < maze[0].length
                          && maze[pos[0] + dir[0] * i][pos[1] + dir[1] * i] == 0) {
                        i++;
                    }
                    i--;
                    
                    if (dis[pos[0] + dir[0] * i][pos[1] + dir[1] * i] > dis[pos[0]][pos[1]] + i) {
                        dis[pos[0] + dir[0] * i][pos[1] + dir[1] * i] = dis[pos[0]][pos[1]] + i;
                        int[] next = new int[]{pos[0] + dir[0] * i, pos[1] + dir[1] * i};
                        que.add(next);
                    }
                }
            }
            
            return dis[destination[0]][destination[1]] == Integer.MAX_VALUE ? -1 : dis[destination[0]][destination[1]];
        }
    }
    

    Solution #2, Dijkastra
    O(m * n * log(m * n)), queue的大小为 m * n, 每次搜索为lg

    下面的实现方式有点问题。检查destination应该在刚刚从queue里出来的时候
    class Solution {
        public int shortestDistance(int[][] maze, int[] start, int[] destination) {
            int[][] dirs = {{1,0},{-1,0},{0,1},{0,-1}};
            int[][] dis = new int[maze.length][maze[0].length];
            
            PriorityQueue<int[]> que = new PriorityQueue<>((a, b) -> a[2] - b[2]);
            que.add(new int[]{start[0], start[1], 0});
            for (int[] row: dis)
                Arrays.fill(row, Integer.MAX_VALUE);
            dis[start[0]][start[1]] = 0;
    
            while (!que.isEmpty()) {
                int[] pos = que.poll();
                
                for (int[] dir : dirs) {
                    int i = 0;
                        
                    while (pos[0] + dir[0] * i >= 0 && pos[0] + dir[0] * i < maze.length
                          && pos[1] + dir[1] * i >= 0 && pos[1] + dir[1] * i < maze[0].length
                          && maze[pos[0] + dir[0] * i][pos[1] + dir[1] * i] == 0) {
                        i++;
                    }
                    i--;
                    
                    int row = pos[0] + dir[0] * i;
                    int col = pos[1] + dir[1] * i;
                    
                    if (destination[0] == row && destination[1] == col) return pos[2] + i;
                    if (dis[row][col] > dis[pos[0]][pos[1]] + i) {
                        
                        dis[row][col] = dis[pos[0]][pos[1]] + i;
                        int[] next = new int[]{ row, col, pos[2] + i};    
                        que.add(next);
                    }
                }
            }
            
            return -1;
        }
    }
    

    Thursday, September 13, 2018

    490. The Maze

    490The Maze
    There is a ball in a maze with empty spaces and walls. The ball can go through empty spaces by rolling updownleft or right, but it won't stop rolling until hitting a wall. When the ball stops, it could choose the next direction.
    Given the ball's start position, the destination and the maze, determine whether the ball could stop at the destination.
    The maze is represented by a binary 2D array. 1 means the wall and 0 means the empty space. You may assume that the borders of the maze are all walls. The start and destination coordinates are represented by row and column indexes.
    Example 1
    Input 1: a maze represented by a 2D array
    
    0 0 1 0 0
    0 0 0 0 0
    0 0 0 1 0
    1 1 0 1 1
    0 0 0 0 0
    
    Input 2: start coordinate (rowStart, colStart) = (0, 4)
    Input 3: destination coordinate (rowDest, colDest) = (4, 4)
    
    Output: true
    Explanation: One possible way is : left -> down -> left -> down -> right -> down -> right.
    
    
    Example 2
    Input 1: a maze represented by a 2D array
    
    0 0 1 0 0
    0 0 0 0 0
    0 0 0 1 0
    1 1 0 1 1
    0 0 0 0 0
    
    Input 2: start coordinate (rowStart, colStart) = (0, 4)
    Input 3: destination coordinate (rowDest, colDest) = (3, 2)
    
    Output: false
    Explanation: There is no way for the ball to stop at the destination.
    
    
    Note:
    1. There is only one ball and one destination in the maze.
    2. Both the ball and the destination exist on an empty space, and they will not be at the same position initially.
    3. The given maze does not contain border (like the red rectangle in the example pictures), but you could assume the border of the maze are all walls.
    4. The maze contains at least 2 empty spaces, and both the width and height of the maze won't exceed 100.
    ---------------------
    Solution #1, DFS. 这个其实也不用backtracking
    比正常的DFS要多一些步骤:
    1. 记录走的方向,
    2. 每个方向都需要visited信息
    3. 检查撞墙
    4. 如果不是墙,继续当前方向

    O(m * n), 最坏情况每一个格子都走一次

    class Solution {
        public boolean hasPath(int[][] maze, int[] start, int[] destination) {
            int m = maze.length, n = maze[0].length;
            boolean[][][] visited = new boolean[m][n][5];
            
            return dfs(maze, destination, visited, start[0] - 1, start[1], 1) ||
                dfs(maze, destination, visited, start[0], start[1] + 1, 2) ||
                dfs(maze, destination, visited, start[0] + 1, start[1], 3) ||
                dfs(maze, destination, visited, start[0], start[1] - 1, 4);
        }
        
        private boolean dfs(int[][] maze, int[] destination, boolean[][][] visited, int row, int col, int dir) {
            if (row < 0 || row >= maze.length || col < 0 || col >= maze[0].length 
                || visited[row][col][dir] || maze[row][col] == 1) return false;
            
            visited[row][col][dir] = true;
            if ((dir == 1 && (row == 0 || maze[row - 1][col] == 1)) || 
               (dir == 3 && (row == maze.length - 1 || maze[row + 1][col] == 1))) {
                
                if (destination[0] == row && destination[1] == col) return true;
                return dfs(maze, destination, visited, row, col - 1, 4) || dfs(maze, destination, visited, row, col + 1, 2); 
            } 
            
            if ((dir == 2 && (col == maze[0].length - 1 || maze[row][col + 1] == 1)) || 
               (dir == 4 && (col == 0 || maze[row][col - 1] == 1))) {
                
                if (destination[0] == row && destination[1] == col) return true;
                return dfs(maze, destination, visited, row - 1, col, 1) || dfs(maze, destination, visited, row + 1, col, 3); 
            }
            
            boolean flag = false;
            if (dir == 1) flag = dfs(maze, destination, visited, row - 1, col, 1);
            if (dir == 2) flag = dfs(maze, destination, visited, row, col + 1, 2);
            if (dir == 3) flag = dfs(maze, destination, visited, row + 1, col, 3); 
            if (dir == 4) flag = dfs(maze, destination, visited, row, col - 1, 4); 
            
            if (flag) return true;
            visited[row][col][dir] = false;
            return false;
        }
    }
    

    DFS,在每一层递归里一直走,直到碰到墙。这是对Solution #1的简化
    这里不用backtracking,因为每次遇到墙之后情况都会往4个方向尝试走。

    class Solution {
    
        public boolean hasPath(int[][] maze, int[] start, int[] destination) {
            int m = maze.length, n = maze[0].length;
            boolean[][] visited = new boolean[m][n];
            int[][] dirs = {{-1,0}, {1,0}, {0,1},{0,-1}};
            
            return dfs(maze, destination, visited, start[0], start[1], dirs);
        }
        
        private boolean dfs(int[][] maze, int[] destination, boolean[][] visited, int row, int col, int[][] dirs) {
            if (visited[row][col]) return false;
            if (destination[0] == row && destination[1] == col) return true;
            visited[row][col] = true;
            
            for (int[] dir : dirs) {
                int i = 1;
                while (row + dir[0] * i >= 0 && row + dir[0] * i < maze.length 
                       && col + dir[1] * i >= 0 && col + dir[1] * i < maze[0].length 
                       && maze[row + dir[0] * i][col + dir[1] * i] != 1) {
                    
                    i++;
                }
                i--;
                if (dfs(maze, destination, visited, row + dir[0] * i, col + dir[1] * i, dirs)) return true;
            }
                    
            // visited[row][col] = true;
            return false;
        }
    }
    

    Solution #3, BFS
    class Solution {
        
        public boolean hasPath(int[][] maze, int[] start, int[] destination) {
            Queue<int[]> que = new LinkedList<>();
            que.add(start);
            int[][] dirs = {{1,0},{-1,0},{0,1},{0,-1}};
            boolean[][] visited = new boolean[maze.length][maze[0].length];
            
            while (!que.isEmpty()) {
                int[] pos = que.poll();
                if (pos[0] == destination[0] && pos[1] == destination[1]) return true;
                if (maze[pos[0]][pos[1]] == 1 || visited[pos[0]][pos[1]]) continue;
                visited[pos[0]][pos[1]] = true;
                
                for (int[] dir : dirs) {
                    int i = 0;
                    while (pos[0] + dir[0] * i >= 0 && pos[0] + dir[0] * i < maze.length
                          && pos[1] + dir[1] * i >= 0 && pos[1] + dir[1] * i < maze[0].length
                          && maze[pos[0] + dir[0] * i][pos[1] + dir[1] * i] == 0) {
                        i++;
                    }
                    i--;
                    int[] next = {pos[0] + dir[0] * i, pos[1] + dir[1] * i};
                    que.add(next);
                }
            }
            
            return false;
        }
    }
    

    Sunday, September 9, 2018

    399. Evaluate Division

    399Evaluate Division
    Equations are given in the format A / B = k, where A and B are variables represented as strings, and k is a real number (floating point number). Given some queries, return the answers. If the answer does not exist, return -1.0.
    Example:
    Given a / b = 2.0, b / c = 3.0.
    queries are: a / c = ?, b / a = ?, a / e = ?, a / a = ?, x / x = ? .
    return [6.0, 0.5, -1.0, 1.0, -1.0 ].
    The input is: vector<pair<string, string>> equations, vector<double>& values, vector<pair<string, string>> queries, where equations.size() == values.size(), and the values are positive. This represents the equations. Return vector<double>.
    According to the example above:
    equations = [ ["a", "b"], ["b", "c"] ],
    values = [2.0, 3.0],
    queries = [ ["a", "c"], ["b", "a"], ["a", "e"], ["a", "a"], ["x", "x"] ]. 
    The input is always valid. You may assume that evaluating the queries will result in no division by zero and there is no contradiction.
    ----------------------
    Solution #1, Graph dfs
    O(n * m), n是graph大小,m是query的数量,应该可以用union-find来优化
    class Solution {
        class Node {
            public String key;
            public Map<Node, Double> neighbors;
            public Node(String key) {
                this.key = key;
                neighbors = new HashMap<>();
            }
        }
        
        public double[] calcEquation(String[][] equations, double[] values, String[][] queries) {
            Map<String, Node> graph = buildGraph(equations, values);
            
            int n = queries.length;
            double[] rt = new double[n];
            
            for (int i = 0; i < queries.length; i++) {
                rt[i] = dfs(graph, queries[i][0], queries[i][1], 1.0, new HashSet<String>());
            }
            
            return rt;
        }
        
        private double dfs(Map<String, Node> graph, String start, String end, double value, Set<String> visited) {
            if (!graph.containsKey(start) || !graph.containsKey(end) || visited.contains(start)) return -1.0;
            if (start.equals(end)) return value;
            
            visited.add(start);
            for (Map.Entry<Node, Double> entry : graph.get(start).neighbors.entrySet()) {
                double rt = dfs(graph, entry.getKey().key, end, value * entry.getValue(), visited);
                if (rt != -1.0) return rt;    
                
            }
            
            return -1.0;
        }
        
        private Map<String, Node> buildGraph(String[][] equations, double[] values) {
            Map<String, Node> graph = new HashMap<>();
            
            for (int i = 0; i < equations.length; i++) {
                String[] pair = equations[i];
                if (!graph.containsKey(pair[0])) {
                    Node node = new Node(pair[0]);
                    graph.put(pair[0], node);
                }
                
                if (!graph.containsKey(pair[1])) {
                    Node node = new Node(pair[1]);
                    graph.put(pair[1], node);
                }
                
                graph.get(pair[0]).neighbors.put(graph.get(pair[1]), values[i]);
                graph.get(pair[1]).neighbors.put(graph.get(pair[0]), 1 / values[i]);
            }
            
            return graph;
        }
    }
    

    Solution #2, Union Find,用被除数当作parent
    ToDo: 加入UF本身的优化:size based

    class Solution {
        
        class Node {
            public String key;
            public double val;
            public Node(String key) {
                this.key = key;
                val = 1;
            }
            
            public Node(String key, double val) {
                this.key = key;
                this.val = val;
            }
        }
        
        public double[] calcEquation(String[][] equations, double[] values, String[][] queries) {
            
            Map<String, Node> map = new HashMap<>();
            Map<String, String> uf = new HashMap<>();
            for (int i = 0; i < equations.length; i++) {
                String[] pair = equations[i];
                if (!map.containsKey(pair[0])) {
                    map.put(pair[0], new Node(pair[0]));
                    uf.put(pair[0], pair[0]);
                }
    
                if (!map.containsKey(pair[1])) {
                    map.put(pair[1], new Node(pair[1]));
                    uf.put(pair[1], pair[1]);
                }
    
                Node parentOf1 = find(uf, map, pair[0]);
                Node parentOf2 = find(uf, map, pair[1]);
    
                if (!parentOf1.key.equals(parentOf2.key)) {
                    uf.put(parentOf2.key, parentOf1.key);
                    map.get(parentOf2.key).val = values[i] * parentOf1.val / parentOf2.val;
                }
            }
    
            double[] rt = new double[queries.length];
            for (int i = 0; i < queries.length; i++) {
                if (!map.containsKey(queries[i][0]) || !map.containsKey(queries[i][1])) {
                    rt[i] = -1.0;
                    continue;
                }
    
                Node p1 = find(uf, map, queries[i][0]);
                Node p2 = find(uf, map, queries[i][1]);
                if (p1.key.equals(p2.key)) {
                    rt[i] = p2.val / p1.val;
                }else {
                    rt[i] = -1.0;
                }
            }
    
            return rt;
        }
            
        private Node find(Map<String, String> uf, Map<String, Node> map, String key) {
            String ori = key;
            double val = map.get(ori).val;
    
            while (!uf.get(key).equals(key)) {
                val *= map.get(uf.get(key)).val;
                key = uf.get(key);
            }
    
            uf.put(ori, key);
            map.get(ori).val = val;
            return new Node(key, val); // Use Node as Pair: return the key of parent, but the value of the original passed in key
        }
    }