Tuesday, February 5, 2019

659. Split Array into Consecutive Subsequences


Greedily append the element to previous sequences
Always try to append
Only create a single sequence if we are not able to append

class Solution {
    public boolean isPossible(int[] nums) {
        // For every distinct number, we keep track of then consective sequence end with num
        // whose length are 1 2 3
        int prev = Integer.MIN_VALUE, p1 = 0, p2 = 0, p3 = 0;
        for (int i = 0; i < nums.length; i++) {
            int curr = nums[i];
            int c1 = 0, c2 = 0, c3 = 0;
            int count = 1;
            // accumulate numbers with same value as curr
            while (i < nums.length - 1 && nums[i + 1] == curr) {
                count++;
                i++;
            }
           
            // cannot append to previous number
            if (curr != prev + 1) {
                if (p1 != 0 || p2 != 0) return false;
                c1 = count;
            } else { // append to p1 first
                if (count < p1 + p2) return false;
                c2 = p1;
                c3 = p2 + Math.min(p3, count - (p1 + p2));
                c1 = Math.max(0, count - (p1 + p2 + p3));
            }
            prev = curr;
            p1 = c1;
            p2 = c2;
            p3 = c3;
        }
        return p1 == 0 && p2 == 0;
    }
}

Monday, February 4, 2019

499. The Maze III



Version #1 Dijastra

几个需要注意的点
首先visited是必须要有的,否则impossible就是infinite loop
但是不应该是个boolean而应该是一个int,因为一个点可以通过不同的path被到达,只要后来的dist <= visited[ny][nx]都可以
因为hole不一定是球可以停住的状态,所以第一次经过不一定是最佳答案
需要存起来然后每次经过的话都去更新

 73.91 %
class Solution {
    class Point implements Comparable<Point> {
        int x, y, dist;
        String path;
        public Point(int y, int x, int dist, String path) {
            this.x = x;
            this.y = y;
            this.dist = dist;
            this.path = path;
        }
        @Override
        public int compareTo(Point p) {
            if (this.dist != p.dist) {
                return this.dist - p.dist;
            } else {
                return this.path.compareTo(p.path);
            }
        }
    }
    private int rows, cols;
    private int[] dx = new int[]{1, 0, -1, 0};
    private int[] dy = new int[]{0, -1, 0, 1};
    private char[] direction = new char[]{'r', 'u', 'l', 'd'};
    public String findShortestWay(int[][] maze, int[] ball, int[] hole) {
        // priorityQueue of int[] poisition + int dist so far
        // when we pop one element out, we have four choices
        // 1.minHeap ordered by dist
        // 2.original point into minHeap
        // polling from minHeap, try 4 directions and walk
        // if we can walk to that direction (move more than 0 step, destination not visited)
        // if we see hole while rolling, return the final steps directly
        // offer into minHeap with destination position and new distance
       
        if (maze == null || maze.length == 0 || maze[0] == null || maze[0].length == 0) {
            return "impossible";
        }
        PriorityQueue<Point> minHeap = new PriorityQueue<>();
        rows = maze.length;
        cols = maze[0].length;
        int[][] visited = new int[rows][cols];
        for (int i = 0; i < rows; i++) {
            Arrays.fill(visited[i], Integer.MAX_VALUE);
        }
        minHeap.offer(new Point(ball[0], ball[1], 0, ""));
        visited[ball[0]][ball[1]] = 0;
        Point minResult = null;
        while (!minHeap.isEmpty()) {
            Point curr = minHeap.poll();
            int x = curr.x;
            int y = curr.y;
            int dist = curr.dist;
            if (minResult != null && dist > minResult.dist) break;
            String path = curr.path;
            for (int i = 0; i < 4; i++) {
                // try to roll to direction
                int step = 0;
                int nx = x, ny = y;
                while (isValid(ny + dy[i], nx + dx[i]) && maze[ny + dy[i]][nx + dx[i]] == 0) {
                    ny += dy[i];
                    nx += dx[i];
                    step++;
                    if (ny == hole[0] && nx == hole[1]) {
                        Point temp = new Point(ny, nx, dist + step, path + direction[i]);
                        if (minResult == null || temp.compareTo(minResult) < 0) {
                            minResult = temp;
                        }
                    }
                }
                if (step > 0 && dist + step <= visited[ny][nx]) {
                    visited[ny][nx] = dist + step;
                    minHeap.offer(new Point(ny, nx, dist + step, path + direction[i]));
                }
               
            }
        }
        return minResult == null ? "impossible" : minResult.path;
    }
   
    private boolean isValid(int y, int x) {
        return x >= 0 && x < cols && y >= 0 && y < rows;
    }
}

494. Target Sum

二刷 06/2022
Version #1 1D DP

不知道为啥intuitively就是觉得特别简单,完全没有想到recursion上面去
思路就是until index i我们有一个map维护之前全部的sum可能性
然后当前有可能+nums[i]/-nums[i],再更新map

Time O(N * t) -> t is the sum of numbers
Space O(t)

Runtime: 64 ms, faster than 55.34% of Java online submissions for Target Sum.
Memory Usage: 42.1 MB, less than 60.80% of Java online submissions for Target Sum.
class Solution {
    public int findTargetSumWays(int[] nums, int target) {
        if (nums == null || nums.length == 0) {
            return 0;
        }
        // key-value of expression until current number
        // value-count of that value
        Map<Integer, Integer> map = new HashMap<>();
        map.put(0, 1);
        for (int i = 0; i < nums.length; i++) {
            Map<Integer, Integer> tempMap = new HashMap<>();
            for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
                int val = entry.getKey() + nums[i];
                tempMap.put(val, tempMap.getOrDefault(val, 0) + entry.getValue());
                val = entry.getKey() - nums[i];
                tempMap.put(val, tempMap.getOrDefault(val, 0) + entry.getValue());
            }
            map = tempMap;
        }
        return map.getOrDefault(target, 0);
    }
}




一刷
0/1 Knapsack Problem

83.05 %
class Solution {
    public int findTargetSumWays(int[] nums, int S) {
        // 2 dimension of states
        // dp[i][j] -> nums[0, i], with sum of j
        // since sum can be negative, we need to add offset to all sums
        int len = nums.length;
        int total = 0;
        for (int num : nums) {
            total += num;
        }
        // partition nums into to sets -> positive set P and negative set N
        // sum(P) - sum(N) = target
        // sum(P) + sum(N) = total
        // 2 sum(P) = S + total
        // sum(P) = (S + total) / 2;
        int target = total + S;
        if (total < S || target % 2 != 0) return 0;
        target /= 2;
        // find a subset sum equals to S + total
        // dp[i][j] -> pick any number from [0, i) -> sum to j
        int[][] dp = new int[len + 1][target + 1];
        for (int i = 0; i <= len; i++) {
            dp[i][0] = 1;
        }
        for (int i = 1; i <= len; i++) {
            for (int s = 0; s <= target; s++) {
                // if we don't pick current nums[i - 1]
                dp[i][s] = dp[i - 1][s];
                if (s >= nums[i - 1]) {
                    dp[i][s] += dp[i - 1][s - nums[i - 1]];
                }
            }
        }
        return dp[len][target];
    }
}

Sunday, February 3, 2019

362. Design Hit Counter




Version #1 Circular Array

69.23 %
class HitCounter {
    int[] time;
    int[] count;
    /** Initialize your data structure here. */
    public HitCounter() {
        // time index = timestamp % 300, we can override the previous value since it has expired
        time = new int[300];
        count = new int[300];
    }
   
    /** Record a hit.
        @param timestamp - The current timestamp (in seconds granularity). */
    public void hit(int timestamp) {
        int index = timestamp % 300;
        if (time[index] != timestamp) {
            time[index] = timestamp;
            count[index] = 1;
        } else {
            count[index]++;
        }
    }
   
    /** Return the number of hits in the past 5 minutes.
        @param timestamp - The current timestamp (in seconds granularity). */
    public int getHits(int timestamp) {
        int result = 0;
        for (int i = 0; i < 300; i++) {
            if (time[i] + 300 > timestamp) {
                result += count[i];
            }
        }
        return result;
    }
}

Saturday, February 2, 2019

304. Range Sum Query 2D - Immutable

二刷 08/2022
Version #1 2D PrefixSum
Time O(MN) for constructor, O(1) for query
Space O(MN)
Runtime: 215 ms, faster than 46.65% of Java online submissions for Range Sum Query 2D - Immutable.
Memory Usage: 127.3 MB, less than 62.68% of Java online submissions for Range Sum Query 2D - Immutable.

class NumMatrix {
    int[][] sums;
    public NumMatrix(int[][] matrix) {
        int rows = matrix.length;
        int cols = matrix[0].length;
        sums = new int[rows][cols];
        for (int y = 0; y < rows; y++) {
            for (int x = 0; x < cols; x++) {
                sums[y][x] = matrix[y][x];
                if (y - 1 >= 0) {
                    sums[y][x] += sums[y - 1][x];
                }
                if (x - 1 >= 0) {
                    sums[y][x] += sums[y][x - 1];
                }
                if (y - 1 >= 0 && x - 1 >= 0) {
                    sums[y][x] -= sums[y - 1][x - 1];
                }
            }
        }
    }
    //   (0,0) (0,1)
    //    -4   -5
    
    public int sumRegion(int row1, int col1, int row2, int col2) {
        //  (row1 - 1, col1 - 1)                     (row1 - 1, col2)
        //                        (row1, col1)       (row1, col2)
        //  (row2, col1 - 1)      (row2, col1)       (row2, col2)
        int sum = sums[row2][col2];
        if (col1 - 1 >= 0) {
            sum -= sums[row2][col1 - 1];
        }
        if (row1 - 1 >= 0) {
            sum -= sums[row1 - 1][col2];
        }
        if (row1 - 1 >= 0 && col1 - 1 >= 0) {
            sum += sums[row1 - 1][col1 - 1];
        }
        return sum;
    }
}

一刷
Version #1 2D PrefixSum

97.29 %
class NumMatrix {

    private int[][] prefixSum;
    private int rows, cols;
    public NumMatrix(int[][] matrix) {
        if (matrix == null || matrix.length == 0 || matrix[0] == null || matrix[0].length == 0) return;
        this.rows = matrix.length;
        this.cols = matrix[0].length;
        prefixSum = new int[rows + 1][cols + 1];
        // prefixSum[i][j] = matrix[i - 1][j - 1] + prefixSum[i - 1][j] + prefixSum[i][j - 1] - prefixSum[i - 1][j - 1]
        for (int i = 1; i <= rows; i++) {
            for (int j = 1; j <= cols; j++) {
                prefixSum[i][j] = matrix[i - 1][j - 1] + prefixSum[i - 1][j] + prefixSum[i][j - 1] - prefixSum[i - 1][j - 1];
            }
        }
    }
 
    public int sumRegion(int row1, int col1, int row2, int col2) {
        if (rows == 0 || cols == 0) return 0;
        // row2,col1      row2,col2
        // row1,col1      row1,col2
        return prefixSum[row2 + 1][col2 + 1] - prefixSum[row2 + 1][col1] - prefixSum[row1][col2 + 1]
            + prefixSum[row1][col1];
    }
}

308. Range Sum Query 2D - Mutable



Version #1 2D Fenwick Tree

77.62 %
class NumMatrix {

    int[][] tree;
    int rows, cols;
    int[][] matrix;
    public NumMatrix(int[][] matrix) {
        if (matrix == null || matrix.length == 0 || matrix[0] == null || matrix[0].length == 0) return;
        this.matrix = matrix;
        rows = matrix.length;
        cols = matrix[0].length;
        tree = new int[rows + 1][cols + 1]; // 1 indexed
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                updateTree(i, j, matrix[i][j]);
            }
        }
    }
   
    public void update(int row, int col, int val) {
        if (rows == 0 || cols == 0) return;
        updateTree(row, col, val - matrix[row][col]);
        matrix[row][col] = val;
    }
   
    private void updateTree(int row, int col, int diff) {
        for (int i = row + 1; i <= rows; i += (i & (-i))) {
            for (int j = col + 1; j <= cols; j += (j & (-j))) {
                tree[i][j] += diff;
            }
        }
    }
   
    public int sumRegion(int row1, int col1, int row2, int col2) {
        if (rows == 0 || cols == 0) return 0;
        // (row2, col1)       (row2, col2)
        // (row1, col1)       (row1, col2)
        return sum(row2, col2) - sum(row2, col1 - 1) - sum(row1 - 1, col2) + sum(row1 - 1, col1 - 1);
    }
   
    private int sum(int row, int col) {
        int sum = 0;
        for (int i = row + 1; i > 0; i -= (i & (-i))) {
            for (int j = col + 1; j > 0; j -= (j & (-j))) {
                sum += tree[i][j];
            }
        }
        return sum;
    }
}

303. Range Sum Query - Immutable



Version #1 PrefixSum

class NumArray {
    // prefixSum i -> sum[0, i)
    private int[] prefixSum;
    public NumArray(int[] nums) {
        this.prefixSum = new int[nums.length + 1];
        for (int i = 1; i <= nums.length; i++) {
            prefixSum[i] = nums[i - 1] + prefixSum[i - 1];
        }
    }
   
    public int sumRange(int i, int j) {
        return prefixSum[j + 1] - prefixSum[i];
    }
}

Friday, February 1, 2019

274. H-Index



Version #1 Bucket sort

See comments
100.00 %
class Solution {
    public int hIndex(int[] citations) {
        if (citations == null || citations.length == 0) return 0;
        int len = citations.length;
        int[] count = new int[len + 1];
        // count[i] -> number of parpers with citations equals to i
        // e.g. i  0  1  2  3  4
        //         1  1     1
        // if (i == len) -> we must have all papers citation larger than or equals to i
        // say we have 5 numbers and they are 6 7 8 9 10
        // since h index cannot exceed len, we can just put all number larger than len into len
        for (int citation : citations) {
            if (citation >= len) {
                count[len]++;
            } else {
                count[citation]++;
            }
        }
        int sum = 0;
        for (int i = len; i >= 0; i--) {
            sum += count[i]; // there are sum parpers whose citations larger than or equals to i
            if (sum >= i) {
                return i;
            }
        }
        return 0;
    }
}