Showing posts with label ToDo. Show all posts
Showing posts with label ToDo. Show all posts

Monday, February 18, 2019

583. Delete Operation for Two Strings

583. Delete Operation for Two Strings
Given two words word1 and word2, find the minimum number of steps required to make word1 and word2 the same, where in each step you can delete one character in either string.
Example 1:
Input: "sea", "eat"
Output: 2
Explanation: You need one step to make "sea" to "ea" and another step to make "eat" to "ea".
Note:
  1. The length of given words won't exceed 500.
  2. Characters in given words can only be lower-case letters.
--------------------
Solution #1
2个单词的长度和 -longest common subsequence * 2

class Solution {
    public int minDistance(String word1, String word2) {
        return word1.length() + word2.length() - 2 * lcs(word1,word2);
    }
    
    private int lcs(String s1, String s2) {
        int m = s1.length(), n = s2.length();
        int[][] dp = new int[m + 1][n + 1];
        
        for (int i = 1; i <= m; i++) {
            for (int j = 1; j <= n; j++) {
                if (s1.charAt(i - 1) == s2.charAt(j - 1)) dp[i][j] = 1 + dp[i - 1][j - 1];
                else dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
            }
        }
        
        return dp[m][n];
    }
}

Solution #2 DP, ToDo
类似edit distance, 方程:
dp[x][y] = dp[x - 1][y - 1] if s1[x] == s2[y]
dp[x][y] = 1 + min(dp[x - 1][y], dp[x][y - 1) if s1[x] != s2[y]

Monday, February 11, 2019

741. Cherry Pickup

741. Cherry Pickup
In a N x N grid representing a field of cherries, each cell is one of three possible integers.

  • 0 means the cell is empty, so you can pass through;
  • 1 means the cell contains a cherry, that you can pick up and pass through;
  • -1 means the cell contains a thorn that blocks your way.

Your task is to collect maximum number of cherries possible by following the rules below:

  • Starting at the position (0, 0) and reaching (N-1, N-1) by moving right or down through valid path cells (cells with value 0 or 1);
  • After reaching (N-1, N-1), returning to (0, 0) by moving left or up through valid path cells;
  • When passing through a path cell containing a cherry, you pick it up and the cell becomes an empty cell (0);
  • If there is no valid path between (0, 0) and (N-1, N-1), then no cherries can be collected.


Example 1:
Input: grid =
[[0, 1, -1],
 [1, 0, -1],
 [1, 1,  1]]
Output: 5
Explanation: 
The player started at (0, 0) and went down, down, right right to reach (2, 2).
4 cherries were picked up during this single trip, and the matrix becomes [[0,1,-1],[0,0,-1],[0,0,0]].
Then, the player went left, up, up, left to return home, picking up one more cherry.
The total number of cherries picked up is 5, and this is the maximum possible.

Note:
  • grid is an N by N 2D array, with 1 <= N <= 50.
  • Each grid[i][j] is an integer in the set {-1, 0, 1}.
  • It is guaranteed that grid[0][0] and grid[N-1][N-1] are not -1.

-------------------------
Solution #1, ref:https://zxi.mytechroad.com/blog/dynamic-programming/leetcode-741-cherry-pickup/
其实题意就是找2条从左上到右下的路径和是最大的。路径可以重叠,但是重叠部分的cherry只能计算一次。
方法是假设有2个人同时从右下出发往右上走,因为y2 = x1 + y1 - x2, 所以我们可以用(x1,y1,x2) 3个参数就能定义一个状态

class Solution {
    public int cherryPickup(int[][] grid) {
        int n = grid.length;
        int[][][] mem = new int[n][n][n];
        for(int i = 0; i < n; i++){
            for(int j = 0; j < n; j++){
                Arrays.fill(mem[i][j], Integer.MIN_VALUE);
            }
        }
        
        return Math.max(0, rec(grid, mem, n - 1, n - 1, n - 1));
    }
    
    private int rec(int[][] grid, int[][][] mem, int x1, int y1, int x2) {
        int y2 = x1 + y1 - x2;
        if (x1 < 0 || y1 < 0 || x2 < 0 || y2 < 0) return -1;
        if (grid[x1][y1] == -1 || grid[x2][y2] == -1) return -1;
        
        if (mem[x1][y1][x2] != Integer.MIN_VALUE) return mem[x1][y1][x2];
        if (x1 == 0 && y1 == 0) return grid[y1][x1];
        
        mem[x1][y1][x2] = Math.max(rec(grid,mem,x1 - 1,y1,x2 - 1), 
                             Math.max(rec(grid,mem,x1,y1 - 1,x2 - 1), 
                                      Math.max(rec(grid,mem,x1,y1 - 1,x2), rec(grid,mem,x1 - 1,y1,x2))));
        if (mem[x1][y1][x2] >= 0) {
            mem[x1][y1][x2] += grid[x1][y1];
            if (x1 != x2) mem[x1][y1][x2] += grid[x2][y2];
        }
        
        return mem[x1][y1][x2];
    } 
}

ToDo
2D空间
https://leetcode.com/problems/cherry-pickup/discuss/109903/Step-by-step-guidance-of-the-O(N3)-time-and-O(N2)-space-solution

Sunday, January 20, 2019

659. Split Array into Consecutive Subsequences

659. Split Array into Consecutive Subsequences
You are given an integer array sorted in ascending order (may contain duplicates), you need to split them into several subsequences, where each subsequences consist of at least 3 consecutive integers. Return whether you can make such a split.
Example 1:
Input: [1,2,3,3,4,5]
Output: True
Explanation:
You can split them into two consecutive subsequences : 
1, 2, 3
3, 4, 5
Example 2:
Input: [1,2,3,3,4,4,5,5]
Output: True
Explanation:
You can split them into two consecutive subsequences : 
1, 2, 3, 4, 5
3, 4, 5
Example 3:
Input: [1,2,3,4,4,5]
Output: False
Note:
  1. The length of the input is in range of [1, 10000]
------------------------
第一次遍历计算出每一个数的个数
第二次遍历,每一个数都有2种可能
1. 插入之前某个连
2. 重新开一个连
贪心,每次都先选择1

needed用来保存现有的连的末尾

class Solution {
    public boolean isPossible(int[] nums) {
        Map<Integer, Integer> frq = new HashMap<>();
        Map<Integer, Integer> needed = new HashMap<>();
        for (int n : nums) {
            frq.put(n, frq.getOrDefault(n, 0) + 1);
        }
        
        for (int n : nums) {
            if (frq.get(n) == 0) continue;
            if (needed.getOrDefault(n,0) > 0) {
                needed.put(n, needed.get(n) - 1);
                needed.put(n + 1, needed.getOrDefault(n + 1, 0) + 1);
            }else if (frq.getOrDefault(n + 1, 0) > 0 
                      && frq.getOrDefault(n + 2, 0) > 0) {
                
                frq.put(n + 1, frq.get(n + 1) - 1);
                frq.put(n + 2, frq.get(n + 2) - 1);
                needed.put(n + 3, needed.getOrDefault(n + 3, 0) + 1);
            }else {
                return false;
            }
            
            frq.put(n, frq.get(n) - 1);
        }
        
        return true;
    }
}

ToDo 其他解法
https://leetcode.com/problems/split-array-into-consecutive-subsequences/discuss/106495/Java-O(n)-time-and-O(1)-space-solution-greedily-extending-shorter-subsequence

Saturday, January 12, 2019

# Knapsack

0-1 背包问题
问题:给大小为n的数组weights[]和values[], 求在不超过W的情况下能取得的value总和最大是多少,每一个数只能最多取一次(或者不取)

本质是排列组合,复杂度O(2^n)
public static void main(String[] ss) {

        int[] a = {1,2,3};
        int[] b = {4,2,1};
        int w = 3;

        System.out.println(knap(a, b, w));

    }

    private static int maxValue = 0;
    private static int knap(int weights[], int values[], int maxW) {
        dfs(weights, values, 0, 0, 0, maxW);
        return maxValue;
    }


    private static void dfs(int[] weights, int[] values, int curV, int curW, int index, int maxWeight) {
        if (curW > maxWeight) {
            return;
        }

        maxValue = Math.max(maxValue, curV);
        for (int i = index; i < weights.length; i++) {
            dfs(weights, values, curV + values[i], curW + weights[i], i + 1, maxWeight);
        }
    }

另一种写法。这种写法更容易看出我们可以用[index, curW] = weight来标记每一个状态(递归树上的结点)
O(2^n)
    private static int knap(int weights[], int values[], int maxW) {
        return dfs(weights, values, 0, 0, maxW);
    }

    private static int dfs(int[] weights, int[] values, int curW, int index, int maxWeight) {
        if (index == weights.length) {
            return 0;
        }
        int max_1 = 0;
        if (curW + weights[index] <= maxWeight) {
            max_1 = dfs(weights, values, curW + weights[index], index + 1, maxWeight) + values[index];
        }
        int max_2 =  dfs(weights, values, curW, index + 1, maxWeight);
        return Math.max(max_1, max_2);
    }

Memoization
O(maxW * n)
private static int knap(int weights[], int values[], int maxW) {
        int n = weights.length;
        int[][] dp = new int[n][maxW + 1];

        return dfs(weights, values, 0, 0, maxW, dp);
    }

    private static int dfs(int[] weights, int[] values, int curW, int index, int maxWeight, int[][] dp) {
        if (index == weights.length) {
            return 0;
        }

        if (dp[index][curW] != 0) {
            return dp[index][curW];
        }
        int max_1 = 0;
        if (curW + weights[index] <= maxWeight) {
            max_1 = dfs(weights, values, curW + weights[index], index + 1, maxWeight, dp) + values[index];
        }
        int max_2 =  dfs(weights, values, curW, index + 1, maxWeight, dp);
        int rt = Math.max(max_1, max_2);
        dp[index][curW] = rt;

        return rt;
    }

iterative dp, 通项公式跟递归是一摸一样
O(maxW * n)
private static int knap(int weights[], int values[], int maxW) {
        int n = weights.length;
        int[][] dp = new int[n][maxW + 1];

        for (int i = 0; i < maxW + 1; i++) {
            if (weights[0] <= i) {
                dp[0][i] = values[0];
            }
        }

        for (int i = 1; i < n; i ++) {
            for (int j = 1; j < maxW + 1; j++) {
                if (j - weights[i] >= 0) {
                    dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - weights[i]] + values[i]);
                }else {
                    dp[i][j] = dp[i - 1][j];
                }
            }
        }

        return dp[n - 1][maxW];
    }
可以发现当前的dp[row]只依赖于之前一行dp[row - 1], 所以可以简化为一维dp
    private static int knap(int weights[], int values[], int maxW) {
        int n = weights.length;
        int[] dp1 = new int[maxW + 1];
        int[] dp2 = new int[maxW + 1];

        for (int i = 0; i < maxW + 1; i++) {
            if (weights[0] <= i) {
                dp1[i] = values[0];
            }
        }
        
        for (int i = 1; i < n; i ++) {
            for (int j = 1; j < maxW + 1; j++) {
                if (j - weights[i] >= 0) {
                    dp2[j] = Math.max(dp1[j], dp1[j - weights[i]] + values[i]);
                }else {
                    dp2[j] = dp1[j];
                }
            }

            int[] tmp = dp1;
            dp1 = dp2;
            dp2 = tmp;
            Arrays.fill(dp2, 0);
        }

        return dp1[maxW];
    }

Unbounded knapsack
如果按0-1的解法的话会形成3个嵌套的for循环
换一种思路
 , , 
O(n * maxW)
private static int knap(int weights[], int values[], int maxW) {
        int n = weights.length;
        int[] dp = new int[maxW + 1];

        for (int i = 1; i < maxW + 1; i++) {
            for (int j = 0; j < n; j++) {
                if (i >= weights[j]) {
                    dp[i] = Math.max(dp[i], dp[i - weights[j]] + values[j]);
                }
            }
        }

        return dp[maxW];
    }
       

ToDo
可以用贪心算法吗?v / w 最大的那个一直加

Bounded knapsack

Sunday, November 25, 2018

527. Word Abbreviation

527. Word Abbreviation
Given an array of n distinct non-empty strings, you need to generate minimal possible abbreviations for every word following rules below.
  1. Begin with the first character and then the number of characters abbreviated, which followed by the last character.
  2. If there are any conflict, that is more than one words share the same abbreviation, a longer prefix is used instead of only the first character until making the map from word to abbreviation become unique. In other words, a final abbreviation cannot map to more than one original words.
  3. If the abbreviation doesn't make the word shorter, then keep it as original.
Example:
Input: ["like", "god", "internal", "me", "internet", "interval", "intension", "face", "intrusion"]
Output: ["l2e","god","internal","me","i6t","interval","inte4n","f2e","intr4n"]
Note:
  1. Both n and the length of each word will not exceed 400.
  2. The length of each word is greater than 1.
  3. The words consist of lowercase English letters only.
  4. The return answers should be in the same order as the original array.
--------------------
又又又是一道题意模糊。仔细看给的例子,"internal, interval" 不会变成"i6l, in5l"。
再考虑这个例子:
"["abcdefg","abccefg","abcckkg"]" - > ["abcd2g","abccefg","abcckkg"]
["aabacd","aabbcd","aabbad"] -> ["aabacd","aabbcd","aabbad"]

ToDo ref: https://leetcode.com/problems/word-abbreviation/solution/

Solution #1, brute force
O(n ^ 2), n为dict大小

class Solution {
    public List<String> wordsAbbreviation(List<String> dict) {
        int n = dict.size();
        int[] prefix = new int[n];
        String[] rt = new String[n];
        
        for (int i = 0; i < n; i++) {
            prefix[i] = 1;
            rt[i] = abbre(dict.get(i), 1);
        }
                
        for (int i = 0; i < n; i++) {
            String s = rt[i];
            
            while (true) {
                List<Integer> dup = new ArrayList<>();
                
                for (int j = i + 1; j < n; j++) {
                    if (rt[j].equals(rt[i])) {
                        dup.add(j);
                    }
                }

                if (dup.size() == 0) break;
                dup.add(i);
                for (int d : dup) {
                    rt[d] = abbre(dict.get(d), prefix[d]);
                    prefix[d]++;
                }
            }
            
        }
        
        return Arrays.asList(rt);
    }
    
    private String abbre(String s, int i) {
        if (s.length() - i < 3) return s;
        StringBuilder rt = new StringBuilder();
        rt.append(s.substring(0, i));
        rt.append(s.length() - 1 - i);
        rt.append(s.charAt(s.length() - 1));
        return rt.toString();
    }
}

Friday, November 16, 2018

753. Cracking the Safe

753. Cracking the Safe
There is a box protected by a password. The password is n digits, where each letter can be one of the first k digits 0, 1, ..., k-1.
You can keep inputting the password, the password will automatically be matched against the last n digits entered.
For example, assuming the password is "345", I can open it when I type "012345", but I enter a total of 6 digits.
Please return any string of minimum length that is guaranteed to open the box after the entire string is inputted.
Example 1:
Input: n = 1, k = 2
Output: "01"
Note: "10" will be accepted too.
Example 2:
Input: n = 2, k = 2
Output: "00110"
Note: "01100", "10011", "11001" will be accepted too.
Note:
  1. n will be in the range [1, 4].
  2. k will be in the range [1, 10].
  3. k^n will be at most 4096.
----------------
题意是找一个最短的string,这个string得包含所有n长度的排列组合。最优的解是2个排列组合之间只相差一位,如 1234 -> 2345, *234 -> 234*. 解法是把234看作一个结点,12345为这个结点的边,把所有边都遍历一次就可以了
Eulerian path的定义https://en.wikipedia.org/wiki/Eulerian_path
用Hierholzer's algorithm来求path

O(k * k^n) time, Hierholzer's algorithm本来 是O(k^n), 但是以下实现方式每次都对k个边做检查来寻找未走过的边,所以有额外的k消耗
ToDo
class Solution {
    public String crackSafe(int n, int k) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < n; i++) {
            sb.append("0");
        }
        
        Set<String> visited = new HashSet<>();
        visited.add(sb.toString());
        dfs(sb, visited, k, n);
        return sb.toString();
    }
    
    private void dfs(StringBuilder sb, Set<String> visited, int k, int n) {
        String node = sb.substring(sb.length() - n + 1);
        for (int i = k - 1; i >= 0; i--) {
            String s = node + Integer.toString(i);
            if (!visited.contains(s)) {
                visited.add(s);
                sb.append(Integer.toString(i));
                dfs(sb, visited, k, n);
            }
        }
    }
}

Monday, November 12, 2018

818. Race Car

818. Race Car
Your car starts at position 0 and speed +1 on an infinite number line.  (Your car can go into negative positions.)
Your car drives automatically according to a sequence of instructions A (accelerate) and R (reverse).
When you get an instruction "A", your car does the following: position += speed, speed *= 2.
When you get an instruction "R", your car does the following: if your speed is positive then speed = -1 , otherwise speed = 1.  (Your position stays the same.)
For example, after commands "AAR", your car goes to positions 0->1->3->3, and your speed goes to 1->2->4->-1.
Now for some target position, say the length of the shortest sequence of instructions to get there.
Example 1:
Input: 
target = 3
Output: 2
Explanation: 
The shortest instruction sequence is "AA".
Your position goes from 0->1->3.
Example 2:
Input: 
target = 6
Output: 5
Explanation: 
The shortest instruction sequence is "AAARA".
Your position goes from 0->1->3->7->7->6.

Note:
  • 1 <= target <= 10000.
---------------------------
Solution #1, BFS 暴解,内存不不够 case: 330

O(2^n)
class Solution {
    public int racecar(int target) {
        Queue<Node> que = new LinkedList<>();
        Node root = new Node(0, 1, 0);
        que.add(root);
        
        while (!que.isEmpty()) {
            Node node = que.poll();
            if (node.pos == target) return node.len;
            que.add(new Node(node.pos + node.speed, node.speed * 2, node.len + 1));
            que.add(new Node(node.pos, node.speed > 0 ? -1 : 1, node.len + 1));
        }
        
        return 0;
    }
    
    class Node{
        public int pos;
        public int speed;
        public int len;
        public Node(int pos, int speed, int len) {
            this.pos = pos;
            this.speed = speed;
            this.len = len;
        }
    }
}

Solution #2, 加了优化 关键在于剪枝:Math.abs(nextPos - target) <= target. 因为起点跟target的距离就是target,如果比这个还远的话,那最终的步数肯定会超过,没有意义,所以要略掉O (n * log n), n为target的⼤小。速度只有log n种可能,因为速度永远是2的幂次⽅ O(n * log n) 空间,最坏情况是把所有pos和speed的组合都放在queue⾥

class Solution {
    public int racecar(int target) {
        Queue<Node> que = new LinkedList<>();
        Node root = new Node(0, 1, 0);
        que.add(root);
        Set<String> visited = new HashSet<>();
        visited.add("0#1");
        visited.add("0#-1");
        
        while (!que.isEmpty()) {
            Node node = que.poll();
            if (node.pos == target) return node.len;
            int nextPos = node.pos + node.speed;
            String key1 = nextPos  + "#" + node.speed *2;
            if (!visited.contains(key1) && Math.abs(nextPos - target) <= target) {
                que.add(new Node(nextPos, node.speed * 2, node.len + 1));
                visited.add(key1);                
            }
                
            String key2 = node.pos + "#" + (node.speed > 0 ? -1 : 1);
            if (!visited.contains(key2)) {
                que.add(new Node(node.pos, node.speed > 0 ? -1 : 1, node.len + 1));
                visited.add(key2);
            }
        }
        
        return 0;
    }
    
    class Node{
        public int pos;
        public int speed;
        public int len;
        public Node(int pos, int speed, int len) {
            this.pos = pos;
            this.speed = speed;
            this.len = len;
        }
    }
}

Solution #3 rec + memoiz1tion 暴力枚举。 分3种情况:

  1. 刚好⾛到,target == pos,target刚好是2的幂次⽅
  2. 走过头一步,target < pos (两步就没有必要了,看Solution#2的剪枝) 
  3. ⾛i步回头, 再⾛j步回头, i 属于[0, target], j 属于 [0, target - pos]. 注意变量的取值 

ref: https://leetcode.com/problems/r1ce-c1r/discuss/124326/Summ1ry-of-the-BFS-1nd-DP-solutions-with-intuitive-expl1n1tion

class Solution {
    public int racecar(int target) {
        int[] dp = new int[target + 1];
        Arrays.fill(dp, - 1);
        dp[0] = 0;
        return dfs(target, dp);
    }
    
    private int dfs(int target, int[] dp) {
        if (dp[target] >= 0) return dp[target];
        
        dp[target] = Integer.MAX_VALUE;
        int speed = 1, pos = 0, times = 0;
        for (; pos < target; pos += speed, speed <<= 1, times++) {
            
            for (int revPos = 0, revSpeed = 1, revTimes = 0; revPos < pos; revPos += revSpeed, revSpeed <<= 1, revTimes++) {
                dp[target] = Math.min(dp[target], times + 2 + revTimes + dfs(revPos + target - pos, dp));
            }
        }
        
        if (target == pos) {
            dp[target] = Math.min(dp[target], times);    
        }else {
            dp[target] = Math.min(dp[target], times + 1 + dfs(pos - target, dp));
        }
        
        return dp[target];
    }
}

Solution #4 iterative的⽅法,ToDo

465. Optimal Account Balancing

465. Optimal Account Balancing
A group of friends went on holiday and sometimes lent each other money. For example, Alice paid for Bill's lunch for $10. Then later Chris gave Alice $5 for a taxi ride. We can model each transaction as a tuple (x, y, z) which means person x gave person y $z. Assuming Alice, Bill, and Chris are person 0, 1, and 2 respectively (0, 1, 2 are the person's ID), the transactions can be represented as [[0, 1, 10], [2, 0, 5]].
Given a list of transactions between a group of people, return the minimum number of transactions required to settle the debt.
Note:
  1. A transaction will be given as a tuple (x, y, z). Note that x ≠ y and z > 0.
  2. Person's IDs may not be linear, e.g. we could have the persons 0, 1, 2 or we could also have the persons 0, 2, 6.
Example 1:
Input:
[[0,1,10], [2,0,5]]

Output:
2

Explanation:
Person #0 gave person #1 $10.
Person #2 gave person #0 $5.

Two transactions are needed. One way to settle the debt is person #1 pays person #0 and #2 $5 each.
Example 2:
Input:
[[0,1,10], [1,0,1], [1,2,5], [2,0,5]]

Output:
1

Explanation:
Person #0 gave person #1 $10.
Person #1 gave person #0 $1.
Person #1 gave person #2 $5.
Person #2 gave person #0 $5.

Therefore, person #1 only need to give person #0 $4, and all debt is settled.
------------------------
ToDo*再研究⼀下
dfs 返回的是 [i, end]这⼀段所最⼩小交易易数
参考http://www.mathmeth.com/tom/files/settling-debts.pdf 题意求最少的交易易次数。


class Solution {
    public int minTransfers(int[][] transactions) {
        Map<Integer, Integer> map = new HashMap<>();
        
        for (int[] i : transactions) {
            map.put(i[0], map.getOrDefault(i[0], 0) + i[2]);
            map.put(i[1], map.getOrDefault(i[1], 0) - i[2]);
        }
        
        List<Integer> debts = new ArrayList<>(map.values());
        return dfs(debts, 0); // 为什么选0?
    }
    
    private int dfs(List<Integer> debts, int start) {
        while (start < debts.size() && debts.get(start) == 0) {
            start++; // 遇到0后继续往下
        }
        
        if (start == debts.size()) return 0;
        int rt = Integer.MAX_VALUE;
        
        for (int i = start + 1; i < debts.size(); i++) {
            if (debts.get(start) * debts.get(i) < 0) {
                debts.set(i, debts.get(i) + debts.get(start));
                rt = Math.min(rt, 1 + dfs(debts, start + 1));
                debts.set(i, debts.get(i) - debts.get(start));
            }
        }
        
        return rt;
    }
}

变种:发现帐号不平衡后,怎么去平均
找一个中间人(可以为任意的人),其他人都给他转钱或取钱。
follow-up:优化。2种方法
1. 最少交易次数(就是LC这题了)
2. 最少交易额度, 预处理之后,用中间人的算法

from above ref:
1. generality (arbitrary number of lenders),
2. simplicity and practical feasibility,
3. minimized total amount transferred,
4. minimized total number of transfers, and
5. mathematical complexity of obtaining a solution.
There are, however, many other issues that might be considered, such as
1. charging interest on loans,
2. handling exchange rates for multiple currencies, and
3. dealing with distrust among the lenders.

Saturday, October 6, 2018

329. Longest Increasing Path in a Matrix

329. Longest 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),同时记录步数

Sunday, September 30, 2018

630. Course Schedule III

630. Course 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, August 23, 2018

269. Alien Dictionary

269. Alien Dictionary
There is a new alien language which uses the latin alphabet. However, the order among letters are unknown to you. You receive a list of non-empty words from the dictionary, where words are sorted lexicographically by the rules of this new language. Derive the order of letters in this language.
Example 1:
Input:
[
  "wrt",
  "wrf",
  "er",
  "ett",
  "rftt"
]

Output: "wertf"
Example 2:
Input:
[
  "z",
  "x"
]

Output: "zx"
Example 3:
Input:
[
  "z",
  "x",
  "z"
] 

Output: "" 

Explanation: The order is invalid, so return "".
Note:
  1. You may assume all letters are in lowercase.
  2. You may assume that if a is a prefix of b, then a must appear before b in the given dictionary.
  3. If the order is invalid, return an empty string.
  4. There may be multiple valid order of letters, return any one of them is fine.
-------------------------
典型的Topological sort

注意以下个例:
1. 有环
2. ["z", "z"]
3. ["wz", "w"]

class Solution {
    
    private boolean circle = false;
    
    public String alienOrder(String[] words) {
        Map<Character, Set<Character>> map = getGraph(words);
        Map<Character, Integer> visited = new HashMap<>();
        
        StringBuilder rt = new StringBuilder();
        for (Character c : map.keySet()) {
            dfs(map, c, visited, rt);    
        }
        
        if (circle) return "";
        return rt.reverse().toString();
    }
    
    private void dfs(Map<Character, Set<Character>> map, char cur,  Map<Character, Integer> visited, StringBuilder sb) {

        if (visited.containsKey(cur) && visited.get(cur) == 1) {
            circle = true;
            return;
        }
        if (visited.containsKey(cur) && visited.get(cur) == -1) return;
        
        visited.put(cur, 1);
        for (Character c : map.get(cur)) {
            dfs(map, c, visited, sb);
        }    

        visited.put(cur, -1);
        sb.append(cur);
    }
    
    private Map<Character, Set<Character>> getGraph(String[] words) {
        Map<Character, Set<Character>> map = new HashMap<>();
        
        for (String w : words) {
            for (int i = 0; i < w.length(); i++) {
                map.put(w.charAt(i), new HashSet<>());
            }    
        }
        
        for (int i = 1; i < words.length; i++) {
            for (int j = 0; j < words[i].length() && j < words[i - 1].length(); j++) {
                char c1 = words[i - 1].charAt(j);
                char c2 = words[i].charAt(j);
                
                if (c1 == c2) continue;
                map.get(c1).add(c2);        
                break;
            }   
        }
        
        return map;
    }
}

ToDo
1.把上面的代码精简以下。
2.用BFS再写一遍

Tuesday, August 21, 2018

772. Basic Calculator III

772. Basic Calculator III
Implement a basic calculator to evaluate a simple expression string.
The expression string may contain open ( and closing parentheses ), the plus + or minus sign -, non-negative integers and empty spaces .
The expression string contains only non-negative integers, +, -, *, / operators , open ( and closing parentheses ) and empty spaces . The integer division should truncate toward zero.
You may assume that the given expression is always valid. All intermediate results will be in the range of [-2147483648, 2147483647].
Some examples:
"1 + 1" = 2
" 6-4 / 2 " = 4
"2*(5+5*2)/3+(6/2+8)" = 21
"(2+6* 3+5- (3*14/7+2)*5)+3"=-12

Note: Do not use the eval built-in library function.

------------------
当处理到括号的时候用递归

1. I,II和III的通解都可以是:把加减号和值,乘除号和值分别预存起来。
2. 遇到数字计算乘除的值
3.遇到加减号说明乘除已经定下,计算加减号的值

ref:http://shibaili.blogspot.com/2018/08/772-basic-calculator-iii.html

ToDo: 把I,II用类似方法再写一次

class Solution {
    public int calculate(String s) {
        int op1 = 1, op2 = 1;
        int val1 = 0, val2 = 1;
        
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            if (Character.isDigit(c)) {
                int num = c - '0';
                while (i + 1 < s.length() && Character.isDigit(s.charAt(i + 1))) {
                    num = num * 10 + (s.charAt(i + 1) - '0');
                    i++;
                }
                
                val2 = op2 == 1 ? val2 * num : val2 / num;
            }else if (c == '(') {
                int cur = i;
                int count = 0;
                while (i < s.length()) {
                    char ch = s.charAt(i);
                    if (ch == '(') count++;
                    if (ch == ')') count--;
                    if (count == 0) break;
                    i++;
                }
                
                int num = calculate(s.substring(cur + 1,i));
                val2 = op2 == 1 ? val2 * num : val2 / num;
                
            }else if (c == '+' || c == '-') {
                val1 = val1 + op1 * val2;
                op1 = c == '+' ? 1 : -1;
                op2 = 1;
                val2 = 1;
            }else if (c == '*' || c == '/') {
                op2 = c == '*' ? 1 : -1;
            }
        }
        
        return val1 + op1 * val2;
    }
}

Friday, June 29, 2018

394. Decode String

394. Decode String
Given an encoded string, return it's decoded string.
The encoding rule is: k[encoded_string], where the encoded_string inside the square brackets is being repeated exactly k times. Note that k is guaranteed to be a positive integer.
You may assume that the input string is always valid; No extra white spaces, square brackets are well-formed, etc.
Furthermore, you may assume that the original data does not contain any digits and that digits are only for those repeat numbers, k. For example, there won't be input like 3a or 2[4].
Examples:
s = "3[a]2[bc]", return "aaabcbc".
s = "3[a2[c]]", return "accaccacc".
s = "2[abc]3[cd]ef", return "abcabccdcdcdef".

--------------------------------
恶心的题。
思路:处理String分2种情况 1- 是数字 2- 是字母。
数字的话先取得数字,然后对括号里的字母继续进行读取,如果又遇到数字就递归
字母就没啥好说的,吃了就是

ToDo, 用stack再写一次

class Solution {
    public String decodeString(String s) {
        String rt = "";
        int i = 0;
        
        while (i < s.length()) {
            Pair p = nextWord(s, i);
            i = p.i;
            rt += p.s;
        }
            
        return rt;
    }
    
    private Pair getDigits(String s, int i) {
        String d = "";
        while (Character.isDigit(s.charAt(i))) {
            d += s.charAt(i);
            i++;
        }
        i++;
        
        return new Pair(d, i);
    }
    
    private Pair getWord(String s, int i) {
        String st = "";
        while (i < s.length() && s.charAt(i) != ']') {
            if (Character.isDigit(s.charAt(i))) {
                Pair p = nextWord(s, i);
                st += p.s;
                i = p.i;
            } else {
                st += s.charAt(i);
                i++;
            }
        }
        i++;
        
        return new Pair(st, i);
    }
    
    private Pair getSimpleWord(String s, int i) {
        String st = "";
        while (i < s.length() && !Character.isDigit(s.charAt(i)) && s.charAt(i) != ']') {
            st += s.charAt(i);
            i++;
        }

        return new Pair(st, i);
    }
    
    private Pair nextWord(String s, int i) {
        
        if (Character.isDigit(s.charAt(i))) {
        
            Pair digits = getDigits(s, i);
            i = digits.i;
            
            Pair word = getWord(s, i);
            String st = word.s;
            
            int n = Integer.parseInt(digits.s);
            String rt = "";
            for (int j = 0; j < n; j++) {
                rt += st;
            }
            
            return new Pair(rt, word.i);
            
        }else {
            return getSimpleWord(s, i);
        }
    }
}
            
class Pair{
    public String s;
    public int i;
    public Pair(String s, int i) {
        this.s = s;
        this.i = i;
    }
}

Updated on Dec-23rd-2018
跟上面思路一样,区别在于遇到letter的时候就直接加。这样写起来更简单
class Solution {
    private int i = 0;
    public String decodeString(String s) {
        return dfs(s);
    }
    
    private int getDigit(String s) {
        int rt = 0;
        
        while (Character.isDigit(s.charAt(i))) {
            rt = rt * 10 + (s.charAt(i) - '0');
            i++;
        }
        return rt;
    }
    
    private String dfs(String s) {
        StringBuilder sb = new StringBuilder();
        
        while (i < s.length() && s.charAt(i) != ']') {
            char c = s.charAt(i);
            if (Character.isDigit(c)) {
                int times = getDigit(s);
                
                i++; // '['
                String word = dfs(s);
                
                while (times > 0) {
                    sb.append(word);
                    times--;
                }
                
            }else {
                sb.append(c);        
            }
            i++;
        }
        
        return sb.toString();
    }
}

Thursday, June 28, 2018

721 Accounts Merge

721. Accounts Merge
Given a list accounts, each element accounts[i] is a list of strings, where the first element accounts[i][0] is a name, and the rest of the elements are emails representing emails of the account.
Now, we would like to merge these accounts. Two accounts definitely belong to the same person if there is some email that is common to both accounts. Note that even if two accounts have the same name, they may belong to different people as people could have the same name. A person can have any number of accounts initially, but all of their accounts definitely have the same name.
After merging the accounts, return the accounts in the following format: the first element of each account is the name, and the rest of the elements are emails in sorted order. The accounts themselves can be returned in any order.
Example 1:
Input: 
accounts = [["John", "johnsmith@mail.com", "john00@mail.com"], ["John", "johnnybravo@mail.com"], ["John", "johnsmith@mail.com", "john_newyork@mail.com"], ["Mary", "mary@mail.com"]]
Output: [["John", 'john00@mail.com', 'john_newyork@mail.com', 'johnsmith@mail.com'],  ["John", "johnnybravo@mail.com"], ["Mary", "mary@mail.com"]]
Explanation: 
The first and third John's are the same person as they have the common email "johnsmith@mail.com".
The second John and Mary are different people as none of their email addresses are used by other accounts.
We could return these lists in any order, for example the answer [['Mary', 'mary@mail.com'], ['John', 'johnnybravo@mail.com'], 
['John', 'john00@mail.com', 'john_newyork@mail.com', 'johnsmith@mail.com']] would still be accepted.
Note:




  • The length of accounts will be in the range [1, 1000].
  • The length of accounts[i] will be in the range [1, 10].
  • The length of accounts[i][j] will be in the range [1, 30].
  • ---------------------------------
    Union Find
    最后返回的时候繁琐了一点,因为题目要求sort过,跟名字一定要在第一位。
    Todo:分析复杂度
    class Solution {
        public List<List<String>> accountsMerge(List<List<String>> accounts) {
            Map<String, Integer> m = new HashMap<>();
            int n = accounts.size();
            int[] uf = getUF(n);
            
            for (int i = 0; i < accounts.size(); i++) {
    
                List<String> row = accounts.get(i);
                for (int j = 1; j < row.size(); j++) {
                    String email = row.get(j);
                    
                    if (m.containsKey(email) && getRoot(uf, m.get(email)) != getRoot(uf, i)) {
                        int index = m.get(email);
                        uf[getRoot(uf, uf[i])] = getRoot(uf, index);
                        n--;
                    }else {
                        m.put(email, getRoot(uf, i));
                    }
                }
            }
            
            return sortAndReturn(m, uf);
        }
        
        private List<List<String>> sortAndReturn(Map<String, Integer> m, int[] uf) {
            Map<Integer,List<String>> rt = new HashMap<>();
            for (Map.Entry<String, Integer> entry : m.entrySet()) {
                int index = getRoot(uf, entry.getValue());
                if (!rt.containsKey(index)) {
                    rt.put(index, new ArrayList<String>());    
                }
                rt.get(index).add(entry.getKey());
            }
            
            List<List<String>> ret= new ArrayList<>();
            for (List<String> l : rt.values()) {
                List<String> temp = new ArrayList<>(l);
                Collections.sort(temp);
                String name = accounts.get(getRoot(uf, m.get(temp.get(0)))).get(0);
                temp.add(0, name);
                ret.add(temp);
            }
            
            return ret;
        }
        
        private int getRoot(int[] uf, int index) {
            
            while (uf[index] != index) {
                index = uf[index];
                uf[index] = uf[uf[index]];
            }
            
            return index;
        }
        
        private int[] getUF(int n) {
            int[] rt = new int[n];
            for (int i = 0; i < n; i++) {
                rt[i] = i;
            }
            
            return rt;
        }
    }
    

    ToDo, DFS做法,见https://leetcode.com/problems/accounts-merge/solution/