Dynamic Programming(Part 3) - LeetCode

279 阅读1分钟

Q****1025. Divisor Game

Alice and Bob take turns playing a game, with Alice starting first.

Initially, there is a number N on the chalkboard. On each player's turn, that player makes a

move

consisting of:

  • Choosing any x with 0 < x < N and N % x == 0.
  • Replacing the number N on the chalkboard with N - x.

Also, if a player cannot make a move, they lose the game.

Return True if and only if Alice wins the game, assuming both players play optimally.

Example 1:

Input: 2
Output: true
Explanation: Alice chooses 1, and Bob has no more moves.

Example 2:

Input: 3
Output: false
Explanation: Alice chooses 1, Bob chooses 1, and Alice has no more moves.

Note:

  1. 1 <= N <= 1000

解法及注释

class Solution {
    public boolean divisorGame(int N) {
        //Approach 1: analyis 
        return N % 2 == 0;
        
        //Approach 2: use dynamic programming
        //return useDp(N);
    }
    
    private boolean useDp(int N) {
        if(N == 1)
            return false;
        int[] dp = new int[N];
        dp[0] = 0;
        for(int i = 1; i < dp.length; i++){
            dp[i] = N == 1 ? 1 : --N;
        }
        return dp.length % 2 == 0 ? dp[dp.length-1] == 1 : dp[dp.length-1] != 1;
    }
}

Q****1143. Longest Common Subsequence

Given two strings text1 and text2, return the length of their longest common subsequence. A subsequence of a string is a new string generated from the original string with some characters(can be none) deleted without changing the relative order of the remaining characters. (eg, "ace" is a subsequence of "abcde" while "aec" is not). A common subsequence of two strings is a subsequence that is common to both strings.

If there is no common subsequence, return 0.

Example 1:

Input: text1 = "abcde", text2 = "ace" 
Output: 3  
Explanation: The longest common subsequence is "ace" and its length is 3.

Example 2:

Input: text1 = "abc", text2 = "abc"
Output: 3
Explanation: The longest common subsequence is "abc" and its length is 3.

Example 3:

Input: text1 = "abc", text2 = "def"
Output: 0
Explanation: There is no such common subsequence, so the result is 0.

Constraints:

  • 1 <= text1.length <= 1000
  • 1 <= text2.length <= 1000
  • The input strings consist of lowercase English characters only.

解法及注释

class Solution {
    public int longestCommonSubsequence(String text1, String text2) {
        //pre-check
        if(text1 == null || text2 == null) 
            return 0;
        
        if(text1.length() == 0 || text2.length() == 0)
            return 0;
        
        int m = text1.length();
        int n = text2.length();
        
        int[][] res = new int[m+1][n+1];
        for(int i = 1; i <= m; i++) {
            for(int j = 1; j <= n; j++) {
                //if the last character match, res[i][j] = res[i-1][j-1]+1;
                if(text1.charAt(i-1) == text2.charAt(j-1))
                    res[i][j] = res[i-1][j-1] + 1;
                //if the last character does not match, res[i][j] either come from res[i-1][j] or res[i][j-1]
                else 
                    res[i][j] = Math.max(res[i-1][j], res[i][j-1]);
            }            
        }
        return res[m][n];        
    }
}

Q****1277. Count Square Submatrices with All Ones

Given a m * n matrix of ones and zeros, return how many square submatrices have all ones.

Example 1:

Input: matrix =
[
  [0,1,1,1],
  [1,1,1,1],
  [0,1,1,1]
]
Output: 15
Explanation: 
There are 10 squares of side 1.
There are 4 squares of side 2.
There is  1 square of side 3.
Total number of squares = 10 + 4 + 1 = 15.

Example 2:

Input: matrix = 
[
  [1,0,1],
  [1,1,0],
  [1,1,0]
]
Output: 7
Explanation: 
There are 6 squares of side 1.  
There is 1 square of side 2. 
Total number of squares = 6 + 1 = 7.

Constraints:

  • 1 <= arr.length <= 300
  • 1 <= arr[0].length <= 300
  • 0 <= arr[i][j] <= 1

解法及注释

class Solution {
    public int countSquares(int[][] matrix) {
        if(matrix == null)
            return 0;
        int res = 0;
        int row = matrix.length, col = matrix[0].length;
        
        for(int i = 0; i < row; i++) {
            for(int j = 0; j < col; j++) {
                if((i ==0 || j ==0) && matrix[i][j] == 1)
                    res++;
                else if(matrix[i][j] == 1) {
                    matrix[i][j] = Math.min(matrix[i - 1][j - 1], Math.min(matrix[i - 1][j], matrix[i][j - 1])) + 1;
                    res += matrix[i][j];
                }
            }
        }
        return res;
    }
}

Q****1641. Count Sorted Vowel Strings

Given an integer n, return the number of strings of length n that consist only of vowels (a,e,i,o,u) and are lexicographically sorted.

A string s is lexicographically sorted if for all valid i, s[i] is the same as or comes before s[i+1] in the alphabet.

Example 1:

Input: n = 1
Output: 5
Explanation: The 5 sorted strings that consist of vowels only are ["a","e","i","o","u"].

Example 2:

Input: n = 2
Output: 15
Explanation: The 15 sorted strings that consist of vowels only are
["aa","ae","ai","ao","au","ee","ei","eo","eu","ii","io","iu","oo","ou","uu"].
Note that "ea" is not a valid string since 'e' comes after 'a' in the alphabet.

Example 3:

Input: n = 33
Output: 66045

Constraints:

  • 1 <= n <= 50

解法及注释

class Solution {
    public int countVowelStrings(int n) {
        
        int[] res = new int[5];
        for(int i =0; i < 5; i++) 
            res[i] = 1;
        
        for(int j = 1; j <= n; j++) {
            for(int i = 1; i <5; i++)
                res[i] += res[i - 1];
        }
        return res[4];
    }
}

Q****121. Best Time to Buy and Sell Stock

You are given an array prices where prices[i] is the price of a given stock on the ith day.

You want to maximize your profit by choosing a single day to buy one stock and choosing a different day in the future to sell that stock.

Return the maximum profit you can achieve from this transaction. If you cannot achieve any profit, return 0.

Example 1:

Input: prices = [7,1,5,3,6,4]
Output: 5
Explanation: Buy on day 2 (price = 1) and sell on day 5 (price = 6), profit = 6-1 = 5.
Note that buying on day 2 and selling on day 1 is not allowed because you must buy before you sell.

Example 2:

Input: prices = [7,6,4,3,1]
Output: 0
Explanation: In this case, no transactions are done and the max profit = 0.

Constraints:

  • 1 <= prices.length <= 105
  • 0 <= prices[i] <= 104

解法及注释

class Solution {
    public int maxProfit(int[] prices) {
        if(prices == null || prices.length == 0)
            return 0;
        
        int min = Integer.MAX_VALUE;
        int maxProfit = 0;
        for(int i = 0; i < prices.length; i++) {
            if(prices[i] < min)
                min = prices[i];
            else if(prices[i] - min > maxProfit)
                maxProfit = prices[i] - min;
        }
        return maxProfit;
    }
}

Q****123. Best Time to Buy and Sell Stock III

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 at most two transactions.

Note: You may not engage in multiple transactions at the same time (i.e., you must sell the stock before you buy again).

Example 1:

Input: prices = [3,3,5,0,0,3,1,4]
Output: 6
Explanation: Buy on day 4 (price = 0) and sell on day 6 (price = 3), profit = 3-0 = 3.
Then buy on day 7 (price = 1) and sell on day 8 (price = 4), profit = 4-1 = 3.

Example 2:

Input: prices = [1,2,3,4,5]
Output: 4
Explanation: Buy on day 1 (price = 1) and sell on day 5 (price = 5), profit = 5-1 = 4.
Note that you cannot buy on day 1, buy on day 2 and sell them later, as you are engaging multiple transactions at the same time. You must sell before buying again.

Example 3:

Input: prices = [7,6,4,3,1]
Output: 0
Explanation: In this case, no transaction is done, i.e. max profit = 0.

Example 4:

Input: prices = [1]
Output: 0

Constraints:

  • 1 <= prices.length <= 105
  • 0 <= prices[i] <= 105

解法及注释(TimeLimit Exceeds)

class Solution {
    public int maxProfit(int[] prices) {
        if(prices == null || prices.length < 2)
            return 0;
        
        int oneTrans = getMaxProfit(prices, 0, prices.length - 1);
        int twoTrans = 0;
        for(int i = 1; i < prices.length - 1; i++) {
            twoTrans = Math.max(twoTrans, getMaxProfit(prices, 0, i) + getMaxProfit(prices, i + 1, prices.length - 1));
        }
        return Math.max(oneTrans, twoTrans);
    }
    
    private int getMaxProfit(int[] prices, int start, int end) {
        int min = Integer.MAX_VALUE;
        int maxProfit = 0;
        for(int i = start; i <= end; i++) {
            if(prices[i] < min)
                min = prices[i];
            else if(prices[i] - min > maxProfit)
                maxProfit = prices[i] - min;
        }
        return maxProfit;
    }
}

解法及注释

class Solution {
    public int maxProfit(int[] prices) {
        if(prices == null || prices.length < 2)
            return 0;
        
        int buy1 = -prices[0], buy2 = -prices[0];
        int profit1 = 0, total = 0;
        
        for(int price : prices) {
            buy1 = Math.max(buy1, -price);
            profit1 = Math.max(profit1, buy1 + price);
            buy2 = Math.max(buy2, profit1 + (-price));
            total = Math.max(total, buy2 + price);
        }
        return total;
    }
}

Q****188. Best Time to Buy and Sell Stock IV

You are given an integer array prices where prices[i] is the price of a given stock on the ith day.

Design an algorithm to find the maximum profit. You may complete at most k transactions.

Notice that you may not engage in multiple transactions simultaneously (i.e., you must sell the stock before you buy again).

Example 1:

Input: k = 2, prices = [2,4,1]
Output: 2
Explanation: Buy on day 1 (price = 2) and sell on day 2 (price = 4), profit = 4-2 = 2.

Example 2:

Input: k = 2, prices = [3,2,6,5,0,3]
Output: 7
Explanation: Buy on day 2 (price = 2) and sell on day 3 (price = 6), profit = 6-2 = 4. Then buy on day 5 (price = 0) and sell on day 6 (price = 3), profit = 3-0 = 3.

Constraints:

  • 0 <= k <= 100
  • 0 <= prices.length <= 1000
  • 0 <= prices[i] <= 1000

解法及注释

class Solution {
    public int maxProfit(int k, int[] prices) {
        if(prices == null || prices.length == 0 || k <= 0)
            return 0;
        
        int n = prices.length;
        if(k * 2 > n) {
            int res = 0;
            for(int i = 1; i < n; i++) {
                res += Math.max(0, prices[i] - prices[i - 1]);
            }
            return res;
        }
        
        // dp[i][used_k][ishold] = balance
        // ishold: 0 nothold, 1 hold
        int[][][] dp = new int[n][k+1][2];
        
        //initialize the array with minus int value to prevent overflow
        for(int i = 0; i < n; i++) {
            for(int j = 0; j <= k; j++) {
                dp[i][j][0] = -1000000000;
                dp[i][j][1] = -1000000000;
            } 
        }
        
        //set start value
        dp[0][0][0] = 0;
        dp[0][1][1] = -prices[0];
        
        /* 1. Keep holding the stock:
           dp[i][j][1]=dp[i−1][j][1]
           2. Keep not holding the stock:
           dp[i][j][0]=dp[i−1][j][0]
           3. Buying, when j>0:
           dp[i][j][1]=dp[i−1][j−1][0]−prices[i]
           4. Selling
           dp[i][j][0]=dp[i−1][j][1]+prices[i]
           
           We can combine they together to find the maximum profit:
           dp[i][j][0]=max(dp[i−1][j][0],dp[i−1][j][1]+prices[i])
           dp[i][j][1]=max(dp[i−1][j][1],dp[i−1][j−1][0]−prices[i])
        */
        
        for(int i = 1; i < n; i++) {
            for(int j = 0; j <= k; j++) {
                dp[i][j][0] = Math.max(dp[i-1][j][0], dp[i-1][j][1] + prices[i]);
                
                if(j > 0) 
                    dp[i][j][1] = Math.max(dp[i-1][j][1], dp[i-1][j - 1][0] - prices[i]);
            } 
        }
        
        int res = 0;
        for(int i = 0; i <=k; i++) 
            res = Math.max(res, dp[n -1][i][0]);
        return res;
    }
}

Q****309. Best Time to Buy and Sell Stock with Cooldown

Say you have an array for which the _i_th 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:

Input: [1,2,3,0,2]
Output: 3 
Explanation: transactions = [buy, sell, cooldown, buy, sell]

解法及注释

class Solution {
    public int maxProfit(int[] prices) {
        if(prices == null || prices.length < 2)
            return 0;
        
        int n = prices.length;
        int buy = -prices[0], sell = 0, cooldown = 0;
        for(int i = 1; i < n; i++) {
            int tempBuy = Math.max(buy, cooldown - prices[i]);
            //assume cooldown is the value as sell
            cooldown = sell;
            sell = Math.max(sell, buy + prices[i]);
            buy = tempBuy;
        }
        return sell;
    }
}

Q****132. Palindrome Partitioning II

Given a string s, partition s such that every substring of the partition is a palindrome.

Return the minimum cuts needed for a palindrome partitioning of s.

Example 1:

Input: s = "aab"
Output: 1
Explanation: The palindrome partitioning ["aa","b"] could be produced using 1 cut.

Example 2:

Input: s = "a"
Output: 0

Example 3:

Input: s = "ab"
Output: 1

Constraints:

  • 1 <= s.length <= 2000
  • s consists of lower-case English letters only.

**解法及注释
**

class Solution {
    public int minCut(String s) {
        if(s == null || s.length() < 2)
            return 0;
        
        char[] chars = s.toCharArray();
        int len = s.length();
        boolean[][] dp = new boolean[len][len];
        int cut[] = new int[len];
        //single character
        for(int i = 0; i < len; i++) 
            dp[i][i] = true;
        
        //double character
        for(int i = 0; i < len - 1; i++) {
            if(chars[i] == chars[i + 1])
                dp[i][i + 1] = true;
        }
    
        //n characters
        for(int i = 3; i <= len; i++) {
            for(int j = 0; j < len - i + 1; j++) {
                int k = i + j - 1;
                if(chars[j] == chars[k] && dp[j + 1][k - 1])
                    dp[j][k] = true;
            }
        }
        
        
        for(int i = 0; i < len; i++) {
            if(dp[0][i])
                cut[i] = 0; //already palindrome
            else {
                cut[i] = Integer.MAX_VALUE;
                for(int j = 0; j < i; j++) {
                    if((dp[j + 1][i] == true) && (cut[j] + 1 < cut[i]))
                        cut[i] = 1 + cut[j];
                }
            }
        }
        return cut[len - 1];
    }
}

Q****32. Longest Valid Parentheses

Given a string containing just the characters '(' and ')', find the length of the longest valid (well-formed) parentheses substring.

Example 1:

Input: s = "(()"
Output: 2
Explanation: The longest valid parentheses substring is "()".

Example 2:

Input: s = ")()())"
Output: 4
Explanation: The longest valid parentheses substring is "()()".

Example 3:

Input: s = ""
Output: 0

Constraints:

  • 0 <= s.length <= 3 * 104
  • s[i] is '(', or ')'.

解法及注释

public class Solution {
    public int longestValidParentheses(String s) {
        int max = 0;
        int dp[] = new int[s.length()];
        for (int i = 1; i < s.length(); i++) {
            if (s.charAt(i) == ')') {
                if (s.charAt(i - 1) == '(') {
                    dp[i] = (i >= 2 ? dp[i - 2] : 0) + 2;
                } else if (i - dp[i - 1] > 0 && s.charAt(i - dp[i - 1] - 1) == '(') {
                    dp[i] = dp[i - 1] + ((i - dp[i - 1]) >= 2 ? dp[i - dp[i - 1] - 2] : 0) + 2;
                }
                max = Math.max(max, dp[i]);
            }
        }
        return max;
    }
}