Dynamic Programming(Part 1) - LeetCode

124 阅读3分钟

Q****5. Longest Palindromic Substring

Given a string s, return the longest palindromic substring in s.

Example 1:

Input: s = "babad"
Output: "bab"
Note: "aba" is also a valid answer.

Example 2:

Input: s = "cbbd"
Output: "bb"

Example 3:

Input: s = "a"
Output: "a"

Example 4:

Input: s = "ac"
Output: "a"

Constraints:

  • 1 <= s.length <= 1000
  • s consist of only digits and English letters (lower-case and/or upper-case),

解法及注释

class Solution {
    public String longestPalindrome(String s) {
        //pre-check
        if(s == null || s.length() == 0)
            return "";
    
        //use start and end to record position
        int start = 0, end = 0;
        for(int i =0; i < s.length(); i++) {
            int oddLen = getRes(i, i, s);
            int evenLen = getRes(i, i+1, s);
            int len = Math.max(oddLen, evenLen);
            if(len > end - start) {
                start = i - (len - 1)/2;
                end = i + len/2;
            }
        }
        return s.substring(start, end+1);
    }
    
    private int getRes(int left, int right, String str) {
        while(left >=0  && right < str.length() 
                        && str.charAt(left) == str.charAt(right)) {
            left--;
            right++;
        }
        return right - left - 1;
    }
}

Q****42. Trapping Rain Water

Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water it can trap after raining.

Example 1:

Input: height = [0,1,0,2,1,0,1,3,2,1,2,1]
Output: 6
Explanation: The above elevation map (black section) is represented by array 
[0,1,0,2,1,0,1,3,2,1,2,1]. In this case, 6 units of rain water (blue section) 
are being trapped.

Example 2:

Input: height = [4,2,0,3,2,5]
Output: 9

Constraints:

  • n == height.length
  • 0 <= n <= 3 * 104
  • 0 <= height[i] <= 105

解法及注释

class Solution {
    
    //Step 1: Find the maximum height from the left up to index i in the array maxLeft
    //Step 2: Find the maximum height from the right up to index i in the array maxRight
    //Step 3: iterate over the height array and update the res: 
    //        Add min(maxLeft[i], maxRight[i]) - height[i] to res
    public int trap(int[] height) {
        if(height == null || height.length == 0)
            return 0;
        int len = height.length, res = 0;
        int[] maxLeft = new int[len], maxRight = new int[len];
        
        maxLeft[0] = height[0];
        for(int i = 1; i < len; i++) 
            maxLeft[i] = Math.max(height[i], maxLeft[i - 1]);
        
        maxRight[len - 1] = height[len - 1];
        for(int i = len - 2; i >=0; i--)
            maxRight[i] = Math.max(height[i], maxRight[i + 1]);
        
        for(int i = 0; i < len - 1; i++) 
            res += Math.min(maxLeft[i], maxRight[i]) - height[i];
        
        return res;
    }
}

Q****53. Maximum Subarray

Given an integer array nums, find the contiguous subarray (containing at least one number) which has the largest sum and return its sum.

Example 1:

Input: nums = [-2,1,-3,4,-1,2,1,-5,4]
Output: 6
Explanation: [4,-1,2,1] has the largest sum = 6.

Example 2:

Input: nums = [1]
Output: 1

Example 3:

Input: nums = [0]
Output: 0

Example 4:

Input: nums = [-1]
Output: -1

Example 5:

Input: nums = [-100000]
Output: -100000

Constraints:

  • 1 <= nums.length <= 3 * 104
  • -105 <= nums[i] <= 105

Follow up: If you have figured out the O(n) solution, try coding another solution using the divide and conquer approach, which is more subtle.

解法及注释

class Solution {
    public int maxSubArray(int[] nums) {
        if(nums == null || nums.length == 0)
            return 0;
        
        if(nums.length == 1)
            return nums[0];
        
        int[] dp = new int[nums.length];
        dp[0] = nums[0];
        int res = dp[0];
        for(int i = 1; i < nums.length; i++) {
            dp[i] = Math.max(nums[i], nums[i] + dp[i - 1]);
            res = Math.max(res, dp[i]);
        }
        return res;
    }
}

Q****62. Unique Paths

A robot is located at the top-left corner of a m x n grid (marked 'Start' in the diagram below).

The robot can only move either down or right at any point in time. The robot is trying to reach the bottom-right corner of the grid (marked 'Finish' in the diagram below).

How many possible unique paths are there?

Example 1:

Input: m = 3, n = 7
Output: 28

Example 2:

Input: m = 3, n = 2
Output: 3
Explanation:
From the top-left corner, there are a total of 3 ways to reach the bottom-right corner:
1. Right -> Down -> Down
2. Down -> Down -> Right
3. Down -> Right -> Down

Example 3:

Input: m = 7, n = 3
Output: 28

Example 4:

Input: m = 3, n = 3
Output: 6

Constraints:

  • 1 <= m, n <= 100
  • It's guaranteed that the answer will be less than or equal to 2 * 109.

解法及注释

class Solution {
    public int uniquePaths(int m, int n) {
        if(m < 1 || n < 1) {
            return 0;
        }
        
        int[][] nums = new int[n+1][m+1];
        for(int i = 1; i < n + 1; i++) {
            for(int j = 1; j < m + 1; j++) {
                if(i == 1 && j == 1) {
                    nums[i][j] = 1;
                }
                else 
                    nums[i][j] = nums[i-1][j] + nums[i][j-1];
            }
        }
        return nums[n][m];

    }
    
    //This is recursion and provide another idea
    private int recursion(int m, int n) {
        if(m == 1 || n == 1) {
            return 1;
        }
        if(m == 2 && n == 2) {
            return 2;
        }
        
        return uniquePaths(m-1, n) + uniquePaths(m, n-1);
    }
}

Q****63. Unique Paths II

A robot is located at the top-left corner of a m x n grid (marked 'Start' in the diagram below).

The robot can only move either down or right at any point in time. The robot is trying to reach the bottom-right corner of the grid (marked 'Finish' in the diagram below).

Now consider if some obstacles are added to the grids. How many unique paths would there be?

An obstacle and space is marked as 1 and 0 respectively in the grid.

Example 1:

Input: obstacleGrid = [[0,0,0],[0,1,0],[0,0,0]]
Output: 2
Explanation: There is one obstacle in the middle of the 3x3 grid above.
There are two ways to reach the bottom-right corner:
1. Right -> Right -> Down -> Down
2. Down -> Down -> Right -> Right

Example 2:

Input: obstacleGrid = [[0,1],[0,0]]
Output: 1

Constraints:

  • m == obstacleGrid.length
  • n == obstacleGrid[i].length
  • 1 <= m, n <= 100
  • obstacleGrid[i][j] is 0 or 1.

解法及注释

class Solution {
    public int uniquePathsWithObstacles(int[][] obstacleGrid) {
        int n = obstacleGrid[0].length;
        int m = obstacleGrid.length;
        //pre-check
        if(m < 1 || n < 1 || m > 100 || n > 100)
            return 0;
        
        if(obstacleGrid[0][0] == 1) 
            return 0;
        
        obstacleGrid[0][0] = 1;
        
    
        //Fill the first row 
        for(int i = 1; i < n; i++) {
            obstacleGrid[0][i] = (obstacleGrid[0][i] == 0 && obstacleGrid[0][i-1] == 1) ? 1 : 0;
        }
        
        //Fill the column
        for(int j = 1; j < m; j++) {
            obstacleGrid[j][0] = (obstacleGrid[j][0] == 0 && obstacleGrid[j-1][0] == 1) ? 1 : 0;
        }
            
        for(int i = 1; i < m; i++) {
            for(int j = 1; j < n; j++) {
                if(obstacleGrid[i][j] == 0) 
                    obstacleGrid[i][j] = obstacleGrid[i-1][j] + obstacleGrid[i][j-1];
                else 
                    obstacleGrid[i][j] = 0;
            } 
        }
        
        return obstacleGrid[m-1][n-1];
        
    }
}

Q****64. Minimum Path Sum

Given a m x n grid filled with non-negative numbers, find a path from top left to bottom right, which minimizes the sum of all numbers along its path.

Note: You can only move either down or right at any point in time.

Example 1:

Input: grid = [[1,3,1],[1,5,1],[4,2,1]]
Output: 7
Explanation: Because the path 13111 minimizes the sum.

Example 2:

Input: grid = [[1,2,3],[4,5,6]]
Output: 12

解法及注释

class Solution {
    public int minPathSum(int[][] grid) {
        int height = grid.length, width = grid[0].length;
        for(int i = 1; i < width; i++) 
            grid[0][i] += grid[0][i -1];
        for(int i = 1; i < height; i++)
            grid[i][0] += grid[i - 1][0];
        
        for(int i = 1; i < height; i++) {
            for(int j = 1; j < width; j++) 
                grid[i][j] += Math.min(grid[i - 1][j], grid[i][j - 1]);
        }
        
        return grid[height -1][width - 1];
    }
}

Q****70. Climbing Stairs

You are climbing a staircase. It takes n steps to reach the top.

Each time you can either climb 1 or 2 steps. In how many distinct ways can you climb to the top?

Example 1:

Input: n = 2
Output: 2
Explanation: There are two ways to climb to the top.
1. 1 step + 1 step
2. 2 steps

Example 2:

Input: n = 3
Output: 3
Explanation: There are three ways to climb to the top.
1. 1 step + 1 step + 1 step
2. 1 step + 2 steps
3. 2 steps + 1 step

Constraints:

  • 1 <= n <= 45

解法及注释

class Solution {
    public int climbStairs(int n) {
        if(n == 1)
            return 1;
        int[] dp = new int[n + 1];
        dp[1] = 1;
        dp[2] = 2;
        
        for(int i = 3; i <= n; i++) 
            //coz either move 1 or 2 steps, so below status function
            dp[i] = dp[i - 1] + dp[i - 2];
        return dp[n];
    }
}

Q****96. Unique Binary Search Trees

Given an integer n, return

the number of structurally unique **BST'**s (binary search trees) which has exactly

n nodes of unique values from 1 to n.

Example 1:

Input: n = 3
Output: 5

Example 2:

Input: n = 1
Output: 1

Constraints:

  • 1 <= n <= 19

解法及注释

class Solution {
    Map<Integer, Integer> res = new HashMap();
    public int numTrees(int n) {
        //pre-check
        if(n == 0 || n == 1)
            return 1;
        
        if(res.containsKey(n))
            return res.get(n);
        
        int count = 0;
        for(int i = 1; i <= n; i++) 
            count += numTrees(i-1) * numTrees(n - i);
        res.put(n, count);
        
        
        return res.get(n);
        
    }
}

Q****95. Unique Binary Search Trees II

Given an integer n, return all the structurally unique **BST'**s (binary search trees), which has exactly n nodes of unique values from 1 to n. Return the answer in any order.

Example 1:

Input: n = 3
Output: [[1,null,2,null,3],[1,null,3,2],[2,1,3],[3,1,null,null,2],[3,2,null,1]]

Example 2:

Input: n = 1
Output: [[1]]

Constraints:

  • 1 <= n <= 8

解法及注释

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public List<TreeNode> generateTrees(int n) {
        //pre-check
        if(n <= 0)
            return Collections.emptyList();
        
        return support(1, n);
    }
    
    private List<TreeNode> support(int start, int end) {
        List<TreeNode> res = new ArrayList();
        if(start > end) {
            res.add(null);
            return res;
        }
        
        for(int i = start; i <= end; i++) {
            List<TreeNode> left = support(start, i - 1);
            List<TreeNode> right = support(i + 1, end);
            for(TreeNode nl : left) {
                for(TreeNode nr : right) {
                    res.add(new TreeNode(i, nl, nr));
                }
            }
        }
        return res;    
    }
}

Q****120. Triangle

Given a triangle array, return the minimum path sum from top to bottom.

For each step, you may move to an adjacent number of the row below. More formally, if you are on index i on the current row, you may move to either index i or index i + 1 on the next row.

Example 1:

Input: triangle = [[2],[3,4],[6,5,7],[4,1,8,3]]
Output: 11
Explanation: The triangle looks like:
   2
  3 4
 6 5 7
4 1 8 3
The minimum path sum from top to bottom is 2 + 3 + 5 + 1 = 11 (underlined above).

Example 2:

Input: triangle = [[-10]]
Output: -10

Constraints:

  • 1 <= triangle.length <= 200
  • triangle[0].length == 1
  • triangle[i].length == triangle[i - 1].length + 1
  • -104 <= triangle[i][j] <= 104

解法及注释

class Solution {
    public int minimumTotal(List<List<Integer>> triangle) {
        if(triangle == null)
            return 0;
        
        //dp solution without introducing extra space,
        //start from the second to the last row
        for(int row = triangle.size() - 2; row >= 0; row--) {
            for(int col = 0; col < triangle.get(row).size(); col++) {
                triangle.get(row).set(col, 
     triangle.get(row).get(col) +  Math.min(triangle.get(row + 1).get(col), triangle.get(row + 1).get(col + 1)));
            }
        }
        return triangle.get(0).get(0);
    }
}

Q****139. Word Break

Given a non-empty string s and a dictionary wordDict containing a list of non-empty words, determine if s can be segmented into a space-separated sequence of one or more dictionary words.

Note:

  • The same word in the dictionary may be reused multiple times in the segmentation.
  • You may assume the dictionary does not contain duplicate words.

Example 1:

Input: s = "leetcode", wordDict = ["leet", "code"]
Output: true
Explanation: Return true because "leetcode" can be segmented as "leet code".

Example 2:

Input: s = "applepenapple", wordDict = ["apple", "pen"]
Output: true
Explanation: Return true because "applepenapple" can be segmented as "apple pen apple".
             Note that you are allowed to reuse a dictionary word.

Example 3:

Input: s = "catsandog", wordDict = ["cats", "dog", "sand", "and", "cat"]
Output: false

解法及注释

class Solution {
    public boolean wordBreak(String s, List<String> wordDict) {
        //recursive approach
        //return recursive(s, wordDict);
        
        //bottom up approach
        return bottomUp(s, wordDict);
    }
    
    //recursive approach exceeds time limit
    private boolean recursive(String s, List<String> wordDict) {
        if(wordDict.contains(s))
            return true;
        for(int i = 1; i <= s.length(); i++) {
            String leftPart = s.substring(0, i);
            if(wordDict.contains(leftPart) && recursive(s.substring(i), wordDict)) 
                return true;
        }
            
        return false;
    }

    private boolean bottomUp(String s, List<String> wordDict) {
        boolean[] dp = new boolean[s.length() + 1];
        Set<String> set = new HashSet(wordDict);
        dp[0] = true;
        
        for(int i = 1; i <= s.length(); i++) {
            for(int j = 0; j < i; j++) {
                if(set.contains(s.substring(j,i)) && dp[j]) {
                    dp[i] = true;
                    break;
                }
            }
        } 
        return dp[s.length()];
    }
}

Q****140. Word Break II

Given a non-empty string s and a dictionary wordDict containing a list of non-empty words, add spaces in s to construct a sentence where each word is a valid dictionary word. Return all such possible sentences.

Note:

  • The same word in the dictionary may be reused multiple times in the segmentation.
  • You may assume the dictionary does not contain duplicate words.

Example 1:

Input:
s = "catsanddog"
wordDict = ["cat", "cats", "and", "sand", "dog"]
Output:
[
  "cats and dog",
  "cat sand dog"
]

Example 2:

Input:
s = "pineapplepenapple"
wordDict = ["apple", "pen", "applepen", "pine", "pineapple"]
Output:
[
  "pine apple pen apple",
  "pineapple pen apple",
  "pine applepen apple"
]
Explanation: Note that you are allowed to reuse a dictionary word.

Example 3:

Input:
s = "catsandog"
wordDict = ["cats", "dog", "sand", "and", "cat"]
Output:
[]

解法及注释

class Solution {
    //holder for a given string, holds possible combination of valid sentences with spaces
    Map<String, List<String>> holder = new HashMap();
    
    public List<String> wordBreak(String s, List<String> wordDict) {
        int max = 0;
        Set<String> set = new HashSet(wordDict);
        for(String word : set) 
            max = Math.max(max, word.length());
        
        return breakStr(s, set, max);
    }
    
    private List<String> breakStr(String s, Set<String> set, int maxLen) {
        if(holder.containsKey(s))
            return holder.get(s);
        
        List<String> res = new ArrayList();
        if(set.contains(s))
            res.add(s);
        
        for(int i = 1; i <= maxLen && i < s.length(); i++) {
            String left = s.substring(0, i);
            
            if(set.contains(left)) {
                //find out all possible valid combination for right part
                List<String> rightSen = breakStr(s.substring(i), set, maxLen);
                
                for(String str : rightSen) {
                    res.add(left+" " + str);
                }
            }
        }
        holder.put(s, res);
        return res;
    }
}

Q****152. Maximum Product Subarray

Given an integer array nums, find the contiguous subarray within an array (containing at least one number) which has the largest product.

Example 1:

Input: [2,3,-2,4]
Output: 6
Explanation: [2,3] has the largest product 6.

Example 2:

Input: [-2,0,-1]
Output: 0
Explanation: The result cannot be 2, because [-2,-1] is not a subarray.

解法及注释

class Solution {
    public int maxProduct(int[] nums) {
        if(nums == null || nums.length == 0)
            return 0;
        
        int large = nums[0];
        for(int num : nums) 
            if(num > large) 
                large = num;
         
        //use 1 as neutral value to save for 0 in middle of the array
        int min = 1, max = 1;
        for(int i = 0; i < nums.length; i++) {
            if(nums[i] == 0) {
                min = 1;
                max = 1;  
                continue;
            }
            int temp = max * nums[i];
            max = Math.max(Math.max(min * nums[i], max * nums[i]), nums[i]);
            min = Math.min(Math.min(min * nums[i], temp), nums[i]);
            large = Math.max(max, large);
        }
        return large;

    }
}

Q****221. Maximal Square

Given an m x n binary matrix filled with 0's and 1's, find the largest square containing only 1's and return its area.

Example 1:

Input: matrix = [["1","0","1","0","0"],["1","0","1","1","1"],["1","1","1","1","1"],["1","0","0","1","0"]]
Output: 4

Example 2:

Input: matrix = [["0","1"],["1","0"]]
Output: 1

Example 3:

Input: matrix = [["0"]]
Output: 0

Constraints:

  • m == matrix.length
  • n == matrix[i].length
  • 1 <= m, n <= 300
  • matrix[i][j] is '0' or '1'.

解法及注释

class Solution {
    //dynamic programming, time complexity O(mn)
    public int maximalSquare(char[][] matrix) {
        if(matrix == null || matrix.length == 0)
            return 0;
    
        int row = matrix.length, col = matrix[0].length;
        int[][] dp = new int[row + 1][col + 1];
        int max = 0;
        for(int i = 1; i <= row; i++) {
            for(int j = 1; j<= col; j++) {
                if(matrix[i - 1][j - 1] == '1') {
                    dp[i][j] = Math.min(Math.min(dp[i - 1][j], dp[i][j - 1]), dp[i - 1][j - 1]) + 1;
                    max = Math.max(max, dp[i][j]);
                }
            }
        }
        return max * max;
    }
}

Q****279. Perfect Squares

Given an integer n, return the least number of perfect square numbers that sum to n.

A perfect square is an integer that is the square of an integer; in other words, it is the product of some integer with itself. For example, 1, 4, 9, and 16 are perfect squares while 3 and 11 are not.

Example 1:

Input: n = 12
Output: 3
Explanation: 12 = 4 + 4 + 4.

Example 2:

Input: n = 13
Output: 2
Explanation: 13 = 4 + 9.

Constraints:

  • 1 <= n <= 104

解法及注释

class Solution {
    public int numSquares(int n) {
        int dp[] = new int[n + 1];
        Arrays.fill(dp, Integer.MAX_VALUE);
        dp[0] = 0;
        for(int i = 0; i <= n; i++) {
            for(int j = 1; i + j * j <= n; j++) {
                dp[i + j * j] = Math.min(dp[i + j * j], dp[i] + 1);
            }
        }
        return dp[n];
    }
}