Sunday, February 7, 2021

265. Paint House II

There are a row of n houses, each house can be painted with one of the k colors. The cost of painting each house with a certain color is different. You have to paint all the houses such that no two adjacent houses have the same color.

The cost of painting each house with a certain color is represented by a n x k cost matrix. For example, costs[0][0] is the cost of painting house 0 with color 0; costs[1][2] is the cost of painting house 1 with color 2, and so on... Find the minimum cost to paint all houses.

Note:
All costs are positive integers.

Example:

Input: [[1,5,3],[2,9,4]]
Output: 5
Explanation: Paint house 0 into color 0, paint house 1 into color 2. Minimum cost: 1 + 4 = 5; 
             Or paint house 0 into color 2, paint house 1 into color 0. Minimum cost: 3 + 2 = 5. 

Follow up:
Could you solve it in O(nk) runtime?


My answer:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
class Solution {
    public int minCostII(int[][] costs) {
        // Solution 1: O(n*k^2) time, space O(nk)
        // dp[n][k]
        // dp[i][0] min cost given color0 is painted to ith house
        // dp[i][1] min cost given color1 is painted to ith house
        // dp[i][2] min cost given color2 is painted to ith house
//         if (costs == null || costs.length == 0) {
//             return 0;
//         }
//         int[][] dp = new int[costs.length][costs[0].length];
//         for (int i = 0; i < costs[0].length; i ++) {
//                 dp[0][i] = costs[0][i];
//         }
//         for (int i = 1; i < costs.length; i ++) {
//             for (int j = 0; j < costs[i].length; j ++) {
//                 int minValidPreCost = Integer.MAX_VALUE;
//                 for (int k = 0; k < costs[i].length; k ++) {
//                     if (j == k) {
//                         continue;
//                     }
//                     minValidPreCost = Math.min(minValidPreCost, dp[i-1][k]);
//                 }
//                 dp[i][j] = costs[i][j] + minValidPreCost;
//             }
//         }
        
//         int minCost = Integer.MAX_VALUE;
//         for (int i = 0; i < dp[costs.length - 1].length; i ++) {
//             minCost = Math.min(minCost, dp[costs.length - 1][i]);
//         }
//         return minCost;
        
        // Solution 2: only keep track of second min and min of previous row
        // because each time we only add min/second min to current row
        // second min is only for index of min
        // time O(nk), space is O(1)
        if (costs == null || costs.length == 0) {
            return 0;
        }

        int preMin = -1;
        int preSecondMin = -1;
        int preMinIdex = -1;

        for (int i = 0; i < costs[0].length; i ++) {
            if (preMin == -1 || costs[0][i] < preMin) {
                preSecondMin = preMin;
                preMin = costs[0][i];
                preMinIdex = i;
            } else if (preSecondMin == -1 || costs[0][i] < preSecondMin) {
                preSecondMin = costs[0][i];
            }
        }

        for (int i = 1; i < costs.length; i ++) {
            int currentMin = -1;
            int currentSecondMin = -1;
            int currentMinIndex = 0;
            for (int j = 0; j < costs[i].length; j ++) {
                int currentCost = costs[i][j];
                if (j != preMinIdex) {
                    currentCost += preMin;
                } else {
                    currentCost += preSecondMin;
                }
                if (currentMin == -1 || currentCost < currentMin) {
                    currentSecondMin = currentMin;
                    currentMin = currentCost;
                    currentMinIndex = j;
                } else if (currentSecondMin == -1 || currentCost < currentSecondMin) {
                    currentSecondMin = currentCost;
                }
            }
            preMin = currentMin;
            preSecondMin = currentSecondMin;
            preMinIdex = currentMinIndex;
        }
        return preMin;
    }
}


Saturday, February 6, 2021

611. Valid Triangle Number

Given an array consists of non-negative integers, your task is to count the number of triplets chosen from the array that can make triangles if we take them as side lengths of a triangle.

Example 1:

Input: [2,2,3,4]
Output: 3
Explanation:
Valid combinations are: 
2,3,4 (using the first 2)
2,3,4 (using the second 2)
2,2,3

Note:

  1. The length of the given array won't exceed 1000.
  2. The integers in the given array are in the range of [0, 1000].

My answer: thought of backtrack in the beginning, didn't think about sorting


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
class Solution {
    private int result = 0;
    public int triangleNumber(int[] nums) {
        // backtrack -- O(n^3) [,aka Cn3]
        // time limit exceeded.
        // List<Integer> singleCombination = new ArrayList<Integer>();
        // helper(nums, 0, singleCombination);
        // return result;
        
        // Solution 2: O(n^2 lg(n))
        // sort, then start with left/right most value,
        // get expected threshold value for triangle,
        // use bs to find the smallest bigger element than threshold

//         int count = 0;
//         Arrays.sort(nums);

//         int l = 0;
//         int r = nums.length - 1;
        
//         for (int i = 0; i < nums.length - 2; i ++) {
//             for (int j = nums.length - 1; j >= i + 2; j --) {
//                 int threshold = nums[j] - nums[i];
//                 // find index of smallest element bigger than threshold
//                 int idx = bs(nums, i, j, threshold);
//                 // if idx == i, meaning threshold is smaller than num[i]
//                 count += idx == i ? j - idx - 1 : j - idx;
//             }
//         }

//         return count;
        
        //Solution 3:
        //sort, start from end, fix it as largest side,
        //find the other 2 sides fromt left elements of end value
        int count = 0;
        Arrays.sort(nums);
        
        for (int k = nums.length - 1; k >= 2; k --) {
            int i = 0;
            int j = k - 1;
            while (i < j) {
                if (nums[i] + nums[j] > nums[k]) {
                    // count options when j,k is fixed
                    count += j - i;
                    j --;
                } else {
                    i ++;
                }
            }
        }
        return count;
    }
    
    private int bs (int[] nums, int l, int r, int threshold) {
        while (l < r) {
            int m = (l + r)/2;
            if (nums[m] > threshold) {
                r = m;
            } else {
                l = m + 1;
            }
        }
        return l;
    }
    
    private void helper(int[] nums, int start, List<Integer> singleCombination) {
        if (singleCombination.size() == 3) {
            if (isValid(singleCombination)) {
                result += 1;
            }
            return;
        }
        
        for (int i = start; i < nums.length; i ++) {
            singleCombination.add(nums[i]);
            helper(nums, i + 1, singleCombination);
            singleCombination.remove(singleCombination.size() - 1);
        }
    }
    
    private boolean isValid(List<Integer> combination) {
        int v0 = combination.get(0);
        int v1 = combination.get(1);
        int v2 = combination.get(2);
        
        if ((v2 + v1 <= v0) || (v2 + v0 <= v1) || (v0 + v1 <= v2) ) {
            return false;
        }
        return true;
    }
}


Tuesday, February 2, 2021

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].
  •  Answer: leverage union found algorithm, from top voted post. Checkout comments below

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    class Solution {
        public List<List<String>> accountsMerge(List<List<String>> acts) {
            Map<String, String> owner = new HashMap<>();
            Map<String, String> parents = new HashMap<>();
            Map<String, TreeSet<String>> unions = new HashMap<>();
            for (List<String> a : acts) {
                for (int i = 1; i < a.size(); i++) {
                    parents.put(a.get(i), a.get(i));
                    owner.put(a.get(i), a.get(0));
                }
            }
            // make each account's emails is pointed to 1 parent email
            // also, if another account with same email,
            // then 2 accounts emails are connected
            // 
            for (List<String> a : acts) {
                String p = find(a.get(1), parents);
                for (int i = 2; i < a.size(); i++)
                    parents.put(find(a.get(i), parents), p);
            }
            // make sure each parent email has its related emails in sorted order
            // this is the key step to merge all emails for each parent
            for(List<String> a : acts) {
                String p = find(a.get(1), parents);
                if (!unions.containsKey(p)) unions.put(p, new TreeSet<>());
                for (int i = 1; i < a.size(); i++)
                    unions.get(p).add(a.get(i));
            }
            List<List<String>> res = new ArrayList<>();
            for (String p : unions.keySet()) {
                List<String> emails = new ArrayList(unions.get(p));
                emails.add(0, owner.get(p));
                res.add(emails);
            }
            return res;
        }
        private String find(String s, Map<String, String> p) {
            return p.get(s) == s ? s : find(p.get(s), p);
        }
    }
    


    277. Find the Celebrity

    Suppose you are at a party with n people (labeled from 0 to n - 1), and among them, there may exist one celebrity. The definition of a celebrity is that all the other n - 1 people know him/her, but he/she does not know any of them.

    Now you want to find out who the celebrity is or verify that there is not one. The only thing you are allowed to do is to ask questions like: "Hi, A. Do you know B?" to get information about whether A knows B. You need to find out the celebrity (or verify there is not one) by asking as few questions as possible (in the asymptotic sense).

    You are given a helper function bool knows(a, b) which tells you whether A knows B. Implement a function int findCelebrity(n). There will be exactly one celebrity if he/she is in the party. Return the celebrity's label if there is a celebrity in the party. If there is no celebrity, return -1.

     

    Example 1:

    Input: graph = [[1,1,0],[0,1,0],[1,1,1]]
    Output: 1
    Explanation: There are three persons labeled with 0, 1 and 2. graph[i][j] = 1 means person i knows person j, otherwise graph[i][j] = 0 means person i does not know person j. The celebrity is the person labeled as 1 because both 0 and 2 know him but 1 does not know anybody.
    

    Example 2:

    Input: graph = [[1,0,1],[1,1,0],[0,1,1]]
    Output: -1
    Explanation: There is no celebrity.
    

     

    Constraints:

    • n == graph.length
    • n == graph[i].length
    • 2 <= n <= 100
    • graph[i][j] is 0 or 1.
    • graph[i][i] == 1

     

    Follow up: If the maximum number of allowed calls to the API knows is 3 * n, could you find a solution without exceeding the maximum number of calls? 

    Answer: answer from top voted post 

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    /* The knows API is defined in the parent class Relation.
          boolean knows(int a, int b); */
    
    public class Solution extends Relation {
        public int findCelebrity(int n) {
            int candidate = 0;
            for(int i = 1; i < n; i++){
                // if candidate knows i, 
                // that being said candidate must not be a celebrity
                // then switch candidate
                if(knows(candidate, i))
                    candidate = i;
            }
            for(int i = 0; i < n; i++){
                if(i != candidate && (knows(candidate, i) || !knows(i, candidate))) return -1;
            }
            return candidate;
        }
    }
    


    528. Random Pick with Weight

    ou are given an array of positive integers w where w[i] describes the weight of ith index (0-indexed).

    We need to call the function pickIndex() which randomly returns an integer in the range [0, w.length - 1]pickIndex() should return the integer proportional to its weight in the w array. For example, for w = [1, 3], the probability of picking the index 0 is 1 / (1 + 3) = 0.25 (i.e 25%) while the probability of picking the index 1 is 3 / (1 + 3) = 0.75 (i.e 75%).

    More formally, the probability of picking index i is w[i] / sum(w).

     

    Example 1:

    Input
    ["Solution","pickIndex"]
    [[[1]],[]]
    Output
    [null,0]
    
    Explanation
    Solution solution = new Solution([1]);
    solution.pickIndex(); // return 0. Since there is only one single element on the array the only option is to return the first element.
    

    Example 2:

    Input
    ["Solution","pickIndex","pickIndex","pickIndex","pickIndex","pickIndex"]
    [[[1,3]],[],[],[],[],[]]
    Output
    [null,1,1,1,1,0]
    
    Explanation
    Solution solution = new Solution([1, 3]);
    solution.pickIndex(); // return 1. It's returning the second element (index = 1) that has probability of 3/4.
    solution.pickIndex(); // return 1
    solution.pickIndex(); // return 1
    solution.pickIndex(); // return 1
    solution.pickIndex(); // return 0. It's returning the first element (index = 0) that has probability of 1/4.
    
    Since this is a randomization problem, multiple answers are allowed so the following outputs can be considered correct :
    [null,1,1,1,1,0]
    [null,1,1,1,1,1]
    [null,1,1,1,0,0]
    [null,1,1,1,0,1]
    [null,1,0,1,0,0]
    ......
    and so on.
    

     

    Constraints:

    • 1 <= w.length <= 10000
    • 1 <= w[i] <= 10^5
    • pickIndex will be called at most 10000 times.

     

    Answer: I just copied answer with top vote. Good idea to convert scale of sum to scale 1. Please checkout my comment below. 

    Btw, Random r = new Random(); r.nextInt(i); // return integer between [0, i)

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    class Solution {
        // explain probabilities[i]:
        // Math.random() is to randomly pick an value from 0 to 1
        // We need to convert from scale 1 to scale sum
        // so that any value of Math.random() corresponds to 1 portion of scale sum
        // in below formula, w[i] is the original value of w
        // probabilities[i] - probabilities[i - 1] == w[i]/sum
        // which means in portion i, its length is w[i]/sum, in the scale of 0 to 1
        // And this is the weighted probability in scale of 0 to 1 
        private double[] probabilities;
        
        public Solution(int[] w) {
            double sum = 0;
            this.probabilities = new double[w.length];
            for(int weight : w)
                sum += weight;
            for(int i = 0; i < w.length; i++){
                w[i] += (i == 0) ? 0 : w[i - 1];
                probabilities[i] = w[i]/sum; 
            }
        }
         
        public int pickIndex() {
            // Arrays.binarySearch returns the index of 1st bigger element
            return Math.abs(Arrays.binarySearch(this.probabilities, Math.random())) - 1;
        }
    }
    
    /**
     * Your Solution object will be instantiated and called as such:
     * Solution obj = new Solution(w);
     * int param_1 = obj.pickIndex();
     */
    


    Monday, February 1, 2021

    254. Factor Combinations

     Numbers can be regarded as product of its factors. For example,

    8 = 2 x 2 x 2;
      = 2 x 4.
    

    Write a function that takes an integer n and return all possible combinations of its factors.

    Note:

    1. You may assume that n is always positive.
    2. Factors should be greater than 1 and less than n.

    Example 1:

    Input: 1
    Output: []
    

    Example 2:

    Input: 37
    Output:[]

    Example 3:

    Input: 12
    Output:
    [
      [2, 6],
      [2, 2, 3],
      [3, 4]
    ]

    Example 4:

    Input: 32
    Output:
    [
      [2, 16],
      [2, 2, 8],
      [2, 2, 2, 4],
      [2, 2, 2, 2, 2],
      [2, 4, 4],
      [4, 8]
    ]


    My answer: didn't figure out solution, only think of sqrt() to restrict choices. Please take a look at the key point below.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    class Solution {
        public List<List<Integer>> getFactors(int n) {
            List<List<Integer>> result = new ArrayList<List<Integer>>();
            helper(2, n , new ArrayList<Integer>(), result);
            return result;
        }
        
        private void helper(int start, int n, List<Integer> singleResult, List<List<Integer>> result) {
            // we use i * i < n to make sure the 1st factor of n is in ascending order
            // and it can avoid duplicate factor
            // because the larger factor corresponding the smaller factor won't be picked
            for (int i = start; i * i <= n; i ++) {
                if (n % i == 0) {// find i as valid factor
                    singleResult.add(i);
                    singleResult.add(n/i);
                    result.add(new ArrayList<Integer>(singleResult));
                    // backtrack singleResult as we are using i as starting factor
                    singleResult.remove(singleResult.size() - 1);
                    // continue to get factors for n/i
                    // KEY POINT: new start is i
                    // because factor smaller than i must have already shown up in
                    // previous recursion, no need to go over again
                    helper(i, n/i, singleResult, result);
                    // backtrack singleResult as we keep using i as starting factor
                    singleResult.remove(singleResult.size() - 1);
                }
            }
        }
    }