AdSense

Tuesday, June 28, 2016

Range 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.

This question is not as easy as it looks like: the time limit will exceed if you just sum up everything when calling the function. A smarter way is to initialize a helper matrix in that each element (x, y) is the sum of matrix from (0, 0) to (x, y). Then whenever we need to call the function, we subtract from the helper matrix to get the answer.


public class NumMatrix {
    private int[][] sumMatrix;
    public NumMatrix(int[][] matrix) {
        if (matrix == null || matrix.length == 0 || matrix[0].length == 0) {
            sumMatrix = null;
        } else {
            sumMatrix = new int[matrix.length][matrix[0].length];
            for (int i = 0; i < matrix.length; i++) {
                sumMatrix[i][0] = matrix[i][0];
            }
            for (int i = 0; i < matrix.length; i++) {
                for (int j = 1; j < matrix[0].length; j++) {
                sumMatrix[i][j] = sumMatrix[i][j - 1] + matrix[i][j];
                }
            }
        
            for (int i = 1; i < matrix.length; i++) {
                for (int j = 0; j < matrix[0].length; j++) {
                    sumMatrix[i][j] += sumMatrix[i - 1][j]; 
                }
            }
        }
        
    }

    public int sumRegion(int row1, int col1, int row2, int col2) {
        if (sumMatrix == null)
            return 0;
        if (row1 == 0 && col1 == 0) {
            return sumMatrix[row2][col2];
        } else if (row1 == 0) {
            return sumMatrix[row2][col2] - sumMatrix[row2][col1 - 1];
        } else if (col1 == 0) {
            return sumMatrix[row2][col2] - sumMatrix[row1 - 1][col2];
        } else {
            return sumMatrix[row2][col2] - sumMatrix[row1 - 1][col2] - sumMatrix[row2][col1 - 1] 
            + sumMatrix[row1 - 1][col1 - 1];
        }
    }
}


Monday, June 27, 2016

Coin Change

You are given coins of different denominations and a total amount of money amount. Write a function to compute the fewest number of coins that you need to make up that amount. If that amount of money cannot be made up by any combination of the coins, return -1.
Example 1:
coins = [1, 2, 5], amount = 11
return 3 (11 = 5 + 5 + 1)
Example 2:
coins = [2], amount = 3
return -1.
Note:
You may assume that you have an infinite number of each kind of coin.

This is a dp problem. For each amount, find the possible combinations from the coins, return if the amount can be formed.


public int coinChange(int[] coins, int amount) {
        if (coins == null || coins.length == 0)
            return -1;
        int[] combinations = new int[amount + 1];
        Arrays.fill(combinations, 1, amount + 1, Integer.MAX_VALUE);
        
        for (int i = 0; i <= amount; i++) {
            for (int j = 0; j < coins.length; j++) {
                if (i + coins[j] <= amount && combinations[i] != Integer.MAX_VALUE) {
                    combinations[i + coins[j]] = Math.min(combinations[i] + 1, combinations[i + coins[j]]);
                }
            }
        }
        return combinations[amount] != Integer.MAX_VALUE ? combinations[amount] : -1;
    }


Additive Number

Additive number is a string whose digits can form additive sequence.
A valid additive sequence should contain at least three numbers. Except for the first two numbers, each subsequent number in the sequence must be the sum of the preceding two.
For example:
"112358" is an additive number because the digits can form an additive sequence: 1, 1, 2, 3, 5, 8.
1 + 1 = 2, 1 + 2 = 3, 2 + 3 = 5, 3 + 5 = 8
"199100199" is also an additive number, the additive sequence is: 1, 99, 100, 199.
1 + 99 = 100, 99 + 100 = 199
Note: Numbers in the additive sequence cannot have leading zeros, so sequence 1, 2, 03 or 1, 02, 3 is invalid.
Given a string containing only digits '0'-'9', write a function to determine if it's an additive number.
Follow up:
How would you handle overflow for very large input integers?
Iterative each combination of first and second number, then recursively check if the whole sequence is valid.

 public boolean isAdditiveNumber(String num) {
        if (num == null || num.length() < 3)
            return false;
        int len = num.length();
        for (int f_pos = 1; f_pos < len; f_pos++) {
            for (int s_pos = f_pos + 1; s_pos < len; s_pos++) {
                if (isValid(num, 0, f_pos, s_pos))
                    return true;
            }
        }
        return false;
    }
    
    private boolean isValid(String num, int start, int f_pos, int s_pos) {
        int len = num.length();
        if ((f_pos - start > 1 && num.charAt(start) == '0') || (s_pos - f_pos > 1 && num.charAt(f_pos) == '0'))
            return false;
        long first = Long.parseLong(num.substring(start, f_pos));
        long second = Long.parseLong(num.substring(f_pos, s_pos));
        String sum = String.valueOf(first + second);
        int next_pos = s_pos + sum.length();
        
        if(next_pos > len || !num.substring(s_pos, next_pos).equals(sum))
            return false;
        return next_pos == len ? true : isValid(num, f_pos, s_pos, next_pos);
        
    }


Sunday, June 26, 2016

Course schedule I and II

There are a total of n courses you have to take, labeled from 0 to n - 1. Some courses may have prerequisites, for example to take course 0 you have to first take course 1, which is expressed as a pair: [0,1] Given the total number of courses and a list of prerequisite pairs, is it possible for you to finish all courses? For example: 2, [[1,0]] There are a total of 2 courses to take. To take course 1 you should have finished course 0. So it is possible. 2, [[1,0],[0,1]] There are a total of 2 courses to take. To take course 1 you should have finished course 0, and to take course 0 you should also have finished course 1. So it is impossible.

The first problem requires us to find if there is a cycle in the graph, if there is, then its a false. It does not require topological sort, yet if you use traditional DFS, it will exceed time limit. However, we can do a little bit optimization to avoid traverse already traversed path. Instead of using a boolean to track visited nodes, we can use an int array. For visited node, we mark it as 1, for the node that is visited and the path that is rooted by it does not contain cycle, we mark it as 2. So along any path if we find a node that is marked as 2, we can save our time and return "true" directly.


 public boolean canFinish(int numCourses, int[][] prerequisites) {
        if (numCourses == 0 || prerequisites == null || prerequisites.length == 0)
            return true;
        Map<Integer, List<Integer>> map = new HashMap<>();
        for (int[] prequisit : prerequisites) {
            int klass = prequisit[0];
            if (!map.containsKey(prequisit[0]))
                map.put(klass, new ArrayList<>());
            map.get(klass).add(prequisit[1]);
        }
        int[] visited = new int[numCourses];
        for (int i = 0; i < numCourses; i++) {
            if (!helper(visited, map, i))
                return false;
        }
        return true;
    }
    
    private boolean helper(int[] visited, Map <Integer, List<Integer>> map, int i) {
        visited[i] = 1;
        //no prerequisit
        if (!map.containsKey(i)) {
            visited[i] = 2;
            return true;
        }
        for (int pre : map.get(i)) {
            if (visited[pre] == 2)
                break;
            if (visited[pre] == 1 || !helper(visited, map, pre))
                return false;
        }
        visited[i] = 2;
        return true;
    } 


The second problem, on the other hand, requires topological sort. Common approach to it is to first find all vertices with indegree 0. Then for each vertex with indegree 0, remove all its neighbor nodes. Iteratively perform this process until all nodes are found.

 public int[] findOrder(int numCourses, int[][] prerequisites) {
        List<Set<Integer>> adjList = new ArrayList<>();
        for (int i = 0; i < numCourses; i++) {
            adjList.add(new HashSet<>());
        }
        for (int i = 0; i < prerequisites.length; i++) {
            adjList.get(prerequisites[i][1]).add(prerequisites[i][0]);
        }
        int[] indegrees = new int[numCourses];
        for (Set<Integer> pre : adjList) {
            for (int i : pre)
                indegrees[i]++;
        }
        Queue<Integer> first = new LinkedList<>();
        for (int i = 0; i < numCourses; i++) {
            if (indegrees[i] == 0)
                first.add(i);
        }
        int[] rst = new int[numCourses];
        int count = 0;
        while (!first.isEmpty()) {
            int curr = first.poll();
            for (int i : adjList.get(curr)) {
                indegrees[i]--;
                if (indegrees[i] == 0)
                    first.add(i);
            }
            rst[count++] = curr;
        }
        return count == numCourses ? rst : new int[]{};
    }


I was considering using DFS for the problem. However, traditional DFS for topological sort requires DAG, which cannot be guaranteed by this problem.

Tuesday, June 14, 2016

Best Time to Buy and Sell Stock with Cooldown

Say you have an array for which the ith element is the price of a given stock on day i.
Design an algorithm to find the maximum profit. You may complete as many transactions as you like (ie, buy one and sell one share of the stock multiple times) with the following restrictions:
  • You may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).
  • After you sell your stock, you cannot buy stock on next day. (ie, cooldown 1 day)
Example:
prices = [1, 2, 3, 0, 2]
maxProfit = 3
transactions = [buy, sell, cooldown, buy, sell]


Interestingly, I was looking at a solution online but it turned out it's too hard to understand, so I figured out my own solution, which is also accepted.

The approach is of course DP (just like other 4 problems). We need two helper array, one is for buy, the other is for sell. Each element in the two array represents the maximum profit got making the transaction.

In each day, either we buy the stock or not. The maximum profit will be the profit we got by selling stock two days ago (cool down for one day) and  the profit we got from yesterday (mot buy).

buyMaxProfit[i] = Math.max(sellMaxProfit[i - 2] - prices[i], buyMaxProfit[i - 1]);

In the sell part, we either sell the stock today or not. The maximum profit would be the profit we buy yesterday plus the price of selling the stock today (sell) or the profit we had yesterday (not sell).

sellMaxProfit[i] = Math.max(buyMaxProfit[i] + prices[i], sellMaxProfit[i - 1]);


    public int maxProfit(int[] prices) {
        int len = prices.length;
        if (len < 2) {
            return 0;
        }
        int[] buyMax = new int[len];
        buyMax[0] = -prices[0];
        buyMax[1] = Math.max(-prices[0], -prices[1]);
        int[] sellMax = new int[len];
        sellMax[0] = 0;
        sellMax[1] = Math.max(0, prices[1] - prices[0]);
        
        for (int i = 2; i < len; i++) {
            sellMax[i] = Math.max(buyMax[i - 1] + prices[i], sellMax[i - 1]);
            buyMax[i] = Math.max(sellMax[i - 2] - prices[i], buyMax[i - 1]);
        }
        return sellMax[len - 1];
    }


Monday, June 13, 2016

Bulb Switcher

There are n bulbs that are initially off. You first turn on all the bulbs. Then, you turn off every second bulb. On the third round, you toggle every third bulb (turning on if it's off or turning off if it's on). For the ith round, you toggle every i bulb. For the nth round, you only toggle the last bulb. Find how many bulbs are on after n rounds.
Example:
Given n = 3. 

At first, the three bulbs are [off, off, off].
After first round, the three bulbs are [on, on, on].
After second round, the three bulbs are [on, off, on].
After third round, the three bulbs are [on, off, off]. 

So you should return 1, because there is only one bulb is on.

Update 2016-10-11

This problem is more like a math problem. For bulb i, if its number of factors is odd, it will be turned on in the end, if its number of factors is even, it will be turned off in the end. However, only the square root of n has odd factors because all other factors exists in pair. For example, n = 36, its factors are (1, 36), (2, 18),  (3, 12), (4, 9), (6, 6), (9, 4), (12, 3), (18, 2), (1, 36).



I don't really like mathematical problems, part of the reason is that I'm not good at it. This problem is a typical math problem. And the code is:

public int bulbSwitch(int n) {
        if (n < 0)
            return 0;
        return (int)Math.sqrt(n);
        
    }


So easy, right? So why this is the case.

For every prime number, it only contains factor of 1 and itself, which means it will ultimately be turned off (turn on at 1st round and turn off at the-number-itself round).
For every square number, in the end it will be turned on because all other factors will be in pairs except its square root. Consider 4:
1 * 4, 2 * 2, 4 * 1
At first round, it will be the forth to turn on, off -> on
At second round, it will be the second to turn off, on -> off
At round four, it will be the first to turn on, off -> on

Consider 9:
1 * 9, 3 * 3, 9 * 1

For all other numbers, it will be turned off ultimately because all the factors are in pairs.

So the number of turned on lights depends on how many square number n has, which is its square root.

Count Numbers with Unique Digits


Given a non-negative integer n, count all numbers with unique digits, x, where 0 ≤ x < 10n.
Example:
Given n = 2, return 91. (The answer should be the total numbers in the range of 0 ≤ x < 100, excluding [11,22,33,44,55,66,77,88,99])

The trick of this problem is to find the pattern of how to find numbers with unique numbers. The code looks rather easy, but very confusing.

i = 1, 10: nothing is dupliated
i = 2, 9 * 9: the first digit has 9 options (exclude 0), the second digit also has 9 options (exclude the number already taken by the first digit, but include 0).
i = 3, 9 * 9 * 8: first two digits follows rules in i = 2, then the third digit has options that exclude all numbers taken by the previous two digits.
.
.
.
i     , 9 * 9 * 8 *... * (10 - i + 1),  the last digit has options that exclude those already chosen by all previous digits.

The final result is the sum of all results.

public int countNumbersWithUniqueDigits(int n) {
        if (n == 0)
            return 1;
        int rst = 10, count = 9;
        for (int i = 2; i <= n; i++) {
            count *= (10 - i + 1);
            rst += count;
        }
        return rst;
    }