算法刷题:LeetCode中常见的动态规划题目

动态规划刷题笔记

53. Maximum Subarray

原题链接地址:https://leetcode.com/problems/maximum-subarray/description/

题目描述

Find the contiguous subarray within an array (containing at least one number) which has the largest sum.

For example, given the array [-2,1,-3,4,-1,2,1,-5,4],
the contiguous subarray [4,-1,2,1] has the largest sum = 6.

题目分析
把原字符串分成很多不同的字串,然后求出字串中最大的。

把原字符串分成很多不同的字串,通过max(f+A[i],A[i])就可以搞定,如果之前的对我没贡献,还不如另起一个字串
设状态为 f[j],表示以 S[j] 结尾的最大连续子序列和,状态转移方程如下:
f=max(f+A[i],A[i]);//对于数组里的一个整数,它只有两种 选择:1、加入之前的 SubArray;2. 自己另起一个 SubArray。
maxsum=max(maxsum,f);// 求字串中最大的

算法设计

public int maxSubArray(int[] A) {
        int n = A.length;
        int[] dp = new int[n];//dp[i] means the maximum subarray ending with A[i];
        dp[0] = A[0];
        int max = dp[0];

        for(int i = 1; i < n; i++){
            dp[i] = A[i] + (dp[i - 1] > 0 ? dp[i - 1] : 0);
            max = Math.max(max, dp[i]);
        }

        return max;
}

另外一种算法设计:

public int maxSubArray(int[] A) {
    int max = A[0], dp = A[0];
    for (int i = 1; i < A.length; i++) {            
        dp = Math.max(dp + A[i] ,A[i]);
        max = Math.max(max, dp);
    }
    return max;
}

第三种算法设计:

class Solution {
public:
    int maxSubArray(int A[], int n) {
        if(0==n) return 0;
        int f=0;//f[j],表示以 A[j] 结尾的最大连续子序列和
        int maxsum=A[0];
        for(int i=0;i<n;++i)
        {

            f=max(f+A[i],A[i]);//是否需要另起一个字串,如果之前的对我没贡献,还不如另起一个字串。
            maxsum=max(maxsum,f); //字串中最大的
        }
        return maxsum;
    }
};

85.Maximal Rectangle

题目链接:https://leetcode.com/problems/maximal-rectangle/description/

题目描述

Given a 2D binary matrix filled with 0’s and 1’s, find the largest rectangle containing only 1’s and return its area.

For example, given the following matrix:

1 0 1 0 0
1 0 1 1 1
1 1 1 1 1
1 0 0 1 0
Return 6.

算法设计

public class Solution {
    public int maximalRectangle(char[][] matrix) {
        int area = 0, new_area, r, l;
        if(matrix.length > 0){
            int[] line = new int[matrix[0].length];
            boolean[] is_processed = new boolean[matrix[0].length];
            for(int i = 0; i < matrix.length; i++){
                for(int j = 0; j < matrix[i].length; j++){
                    if (matrix[i][j] == '1') {
                        line[j]++;
                        is_processed[j] = false;
                    } else {
                        line[j] = 0;
                        is_processed[j] = true;
                    }
                }
                for(int j = 0; j < matrix[i].length; j++){
                    if(is_processed[j]) continue;
                    r = l = 1;
                    while((j + r < line.length)&&(line[j + r] >= line[j])){
                        if(line[j + r] == line[j]) is_processed[j + r] = true;
                        r++;
                    }
                    while((j - l >= 0)&&(line[j - l] >= line[j])) l++;
                    new_area = (r + l - 1)*line[j];
                    if (new_area > area) area = new_area;
                }
            }
        } return area;
    }
}

97.Interleaving String

题目链接:https://leetcode.com/problems/interleaving-string/description/

题目描述

Given s1, s2, s3, find whether s3 is formed by the interleaving of s1 and s2.
For example,
Given:
s1 = “aabcc”,
s2 = “dbbca”,
When s3 = “aadbbcbcac”, return true.
When s3 = “aadbbbaccc”, return false.

题目分析

分析:判断字符串s3是否由s1,s2交叉存取组成

public boolean isInterleave(String s1, String s2, String s3) {

    if ((s1.length()+s2.length())!=s3.length()) return false;

    boolean[][] matrix = new boolean[s2.length()+1][s1.length()+1];

    matrix[0][0] = true;

    for (int i = 1; i < matrix[0].length; i++){
        matrix[0][i] = matrix[0][i-1]&&(s1.charAt(i-1)==s3.charAt(i-1));
    }

    for (int i = 1; i < matrix.length; i++){
        matrix[i][0] = matrix[i-1][0]&&(s2.charAt(i-1)==s3.charAt(i-1));
    }

    for (int i = 1; i < matrix.length; i++){
        for (int j = 1; j < matrix[0].length; j++){
            matrix[i][j] = (matrix[i-1][j]&&(s2.charAt(i-1)==s3.charAt(i+j-1)))
                    || (matrix[i][j-1]&&(s1.charAt(j-1)==s3.charAt(i+j-1)));
        }
    }

    return matrix[s2.length()][s1.length()];

}

DFS解法

To solve this problem, let’s look at if s1[0 ~ i] s2[0 ~ j] can be interleaved to s3[0 ~ k].

Start from indices0, 0, 0 and compare s1[i] == s3[k] or s2[j] == s3[k]
Return valid only if either i or j match k and the remaining is also valid
Caching is the key to performance. This is very similar to top down dp
Only need to cache invalid[i][j] since most of the case s1[0 ~ i] and s2[0 ~ j] does not form s3[0 ~ k]. Also tested caching valid[i][j] the run time is also 1ms
Many guys use substring but it’s duplicate code since substring itself is checking char by char. We are already doing so

public boolean isInterleave(String s1, String s2, String s3) {
    char[] c1 = s1.toCharArray(), c2 = s2.toCharArray(), c3 = s3.toCharArray();
    int m = s1.length(), n = s2.length();
    if(m + n != s3.length()) return false;
    return dfs(c1, c2, c3, 0, 0, 0, new boolean[m + 1][n + 1]);
}

public boolean dfs(char[] c1, char[] c2, char[] c3, int i, int j, int k, boolean[][] invalid) {
    if(invalid[i][j]) return false;
    if(k == c3.length) return true;
    boolean valid = 
        i < c1.length && c1[i] == c3[k] && dfs(c1, c2, c3, i + 1, j, k + 1, invalid) || 
        j < c2.length && c2[j] == c3[k] && dfs(c1, c2, c3, i, j + 1, k + 1, invalid);
    if(!valid) invalid[i][j] = true;
    return valid;
}

120. Triangle

原题链接地址:https://leetcode.com/problems/triangle/description/

题目描述

Given a triangle, find the minimum path sum from top to bottom. Each step you may move to adjacent numbers on the row below.
For example, given the following triangle
[
[2],
[3,4],
[6,5,7],
[4,1,8,3]
]
The minimum path sum from top to bottom is 11 (i.e., 2 + 3 + 5 + 1 = 11).
Note:
Bonus point if you are able to do this using only O(n) extra space, where n is the total number of rows in the triangle.

题目分析

设状态为 f (i; j ),表示从位置 (i; j ) 出发,路径的最小和,
则状态转移方程为:f(i,j)=min{f(i+1,j),f(i+1,j+1)}+(i,j)

算法设计

//算法设计第一种
int minimumTotal(vector<vector<int> > &triangle) {
    int n = triangle.size();
    vector<int> minlen(triangle.back());
    for (int layer = n-2; layer >= 0; layer--) // For each layer
    {
        for (int i = 0; i <= layer; i++) // Check its every 'node'
        {
            // Find the lesser of its two children, and sum the current value in the triangle with it.
            minlen[i] = min(minlen[i], minlen[i+1]) + triangle[layer][i]; 
        }
    }
    return minlen[0];
}

算法设计第二种:

public int minimumTotal(List<List<Integer>> triangle) {
        if(triangle.size() == 0)
            return 0;

        for (int i=triangle.size() - 2; i>=0; i--) {
            for (int j=0; j<=i; j++) {
                List<Integer> nextRow = triangle.get(i+1);
                int sum = Math.min(nextRow.get(j), nextRow.get(j+1)) + triangle.get(i).get(j);
                triangle.get(i).set(j, sum);
            }
        }
        return triangle.get(0).get(0);
    }

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.
For example, given s = “aab”,
Return 1 since the palindrome partitioning [“aa”,”b”] could be produced using 1 cut.

题目分析

题意分析: 对输入的字符串划分为一组回文字符串,最小的分割次数
定义状态 f(i,j) 表示区间 [i,j] 之间最小的 cut 数,则状态转移方程为
这是一个二维函数,实际写代码比较麻烦。
所以要转换成一维 DP。如果每次,从 i 往右扫描,每找到一个回文就算一次 DP 的话,就可以
转换为 f(i)= 区间 [i, n-1] 之间最小的 cut 数,n 为字符串长度,则状态转移方程为

一个问题出现了,就是如何判断 [i,j] 是否是回文?每次都从 i 到 j 比较一遍?太浪费了,这 里也是一个 DP 问题。
定义状态 P[i][j] = true if [i,j] 为回文,那么
P[i][j] = str[i] == str[j] && P[i+1][j-1]

算法设计

算法设计第一种

public int minCut(String s) {
        // validate input
        if (s == null || s.length() <= 1) {
            return 0;
        }
        // dp
        int N = s.length();
        int[] dp = IntStream.range(0, N).toArray(); // initial value: dp[i] = i

        for (int mid = 1; mid <  N; mid++) { // iterate through all chars as mid point of palindrome
            // CASE 1. odd len: center is at index mid, expand on both sides
            for (int start = mid, end = mid; start >= 0 && end < N && s.charAt(start) == s.charAt(end); start--, end++) {
                int newCutAtEnd = (start == 0) ? 0 : dp[start - 1] + 1;
                dp[end] = Math.min(dp[end], newCutAtEnd);
            }
            // CASE 2: even len: center is between [mid-1,mid], expand on both sides
            for (int start = mid - 1, end = mid; start >= 0 && end < N && s.charAt(start) == s.charAt(end); start--, end++) {
                int newCutAtEnd = (start == 0) ? 0 : dp[start - 1] + 1;
                dp[end] = Math.min(dp[end], newCutAtEnd);
            }
        }
        return dp[N - 1];
    }

算法设计第二种

class Solution {
public:
  int minCut(string s)
 {
    const int n = s.size();
    int f[n+1];
    bool p[n][n];
    fill_n(&p[0][0], n * n, false);
//the worst case is cutting by each char
    for (int i = 0; i <= n; i++)
            f[i] = n - 1 - i; // 最后一个 f[n]=-1
    for (int i = n - 1; i >= 0; i--)
    {
        for (int j = i; j < n; j++)
         {
            if (s[i] == s[j] && (j - i < 2 || p[i + 1][j - 1])) 
            { 
                    p[i][j] = true;
                    f[i] = min(f[i], f[j + 1] + 1);
           }
        }
    }
    return f[0];
}
}

算法设计第三种

This can be solved by two points:

cut[i] is the minimum of cut[j – 1] + 1 (j <= i), if [j, i] is palindrome.
If [j, i] is palindrome, [j + 1, i – 1] is palindrome, and c[j] == c[i].
The 2nd point reminds us of using dp (caching).

a b a | c c
j i
j-1 | [j, i] is palindrome
cut(j-1) + 1

public int minCut(String s) {
    char[] c = s.toCharArray();
    int n = c.length;
    int[] cut = new int[n];
    boolean[][] pal = new boolean[n][n];

    for(int i = 0; i < n; i++) {
        int min = i;
        for(int j = 0; j <= i; j++) {
            if(c[j] == c[i] && (j + 1 > i - 1 || pal[j + 1][i - 1])) {
                pal[j][i] = true;  
                min = j == 0 ? 0 : Math.min(min, cut[j - 1] + 1);
            }
        }
        cut[i] = min;
    }
    return cut[n - 1];
}

123.Best Time to Buy and Sell Stock III

题目链接:https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iii/description/

题目描述

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 (ie, you must sell the stock before you buy again).

题目分析

设状态f(i)表示区间[0,i-1]上的最大利润,设置状态g(i),表示区间[i,n-1]上最大利润。
则最大利润为max{f(i)+g(i)};允许在一天内买进又卖出,相当于不交易,因为题目的规定是最多两次,而不是一定要两次。

算法设计

class Solution {
public:
int maxProfit(vector<int>& prices)
 {
    if (prices.size() < 2) return 0;
    const int n = prices.size();
    vector<int> f(n, 0);
    vector<int> g(n, 0);
    for (int i = 1, valley = prices[0]; i < n; ++i) {
        valley = min(valley, prices[i]);
        f[i] = max(f[i - 1], prices[i] - valley);
    }
    for (int i = n - 2, peak = prices[n - 1]; i >= 0; --i) {
        peak = max(peak, prices[i]);
        g[i] = max(g[i], peak - prices[i]);
    }
    int max_profit = 0;
    for (int i = 0; i < n; ++i)
        max_profit = max(max_profit, f[i] + g[i]);
    return max_profit;
    }
};

JAVA代码算法设计

public int maxProfit(int[] prices) {
    // these four variables represent your profit after executing corresponding transaction
    // in the beginning, your profit is 0. 
    // when you buy a stock ,the profit will be deducted of the price of stock.
    int firstBuy = Integer.MIN_VALUE, firstSell = 0;
    int secondBuy = Integer.MIN_VALUE, secondSell = 0;

    for (int curPrice : prices) {
        if (firstBuy < -curPrice) firstBuy = -curPrice; // the max profit after you buy first stock
        if (firstSell < firstBuy + curPrice) firstSell = firstBuy + curPrice; // the max profit after you sell it
        if (secondBuy < firstSell - curPrice) secondBuy = firstSell - curPrice; // the max profit after you buy the second stock
        if (secondSell < secondBuy + curPrice) secondSell = secondBuy + curPrice; // the max profit after you sell the second stock
    }

    return secondSell; // secondSell will be the max profit after passing the prices
}

87.Scramble String

题目链接:https://leetcode.com/problems/scramble-string/description/

64.Minimum Path Sum

题目链接:https://leetcode.com/problems/minimum-path-sum/description/

72.Edit Distance

题目链接:https://leetcode.com/problems/edit-distance/description/

91.Decode Ways

题目链接:https://leetcode.com/problems/decode-ways/description/

639.Decode Ways II

题目链接:https://leetcode.com/problems/decode-ways-ii/description/

115.Distinct Subsequences(不同的子序列)

题目链接:https://leetcode.com/problems/distinct-subsequences/description/

139.Word Break

题目链接:https://leetcode.com/problems/word-break/description/

题目描述

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. You may assume the dictionary does not contain duplicate words.

For example, given
s = “leetcode”,
dict = [“leet”, “code”].

Return true because “leetcode” can be segmented as “leet code”.

UPDATE (2017/1/4):
The wordDict parameter had been changed to a list of strings (instead of a set of strings). Please reload the code definition to get the latest changes.

算法设计

public class Solution {
    public class TrieNode {
        boolean isWord;
        TrieNode[] c;

        public TrieNode(){
            isWord = false;
            c = new TrieNode[128];
        }
    }

    public void addWord(TrieNode t, String w) {
        for (int i = 0; i < w.length(); i++) {
            int j = (int)w.charAt(i); 
            if (t.c[j] == null) t.c[j] = new TrieNode();
            t = t.c[j];
        }
        t.isWord = true;
    }

    public boolean wordBreak(String s, Set<String> wordDict) {
        TrieNode t = new TrieNode(), cur;
        for (String i : wordDict) addWord(t, i);
        char[] str = s.toCharArray();
        int len = str.length;
        boolean[] f = new boolean[len + 1];
        f[len] = true;

        for (int i = len - 1; i >= 0; i--) {
            //System.out.println(str[i]);
            cur = t;
            for (int j = i; cur != null && j < len ; j++) {
                cur = cur.c[(int)str[j]];
                if (cur != null && cur.isWord && f[j + 1]) {
                    f[i] = true;
                    break;
                }
            }
        }
        return f[0];
    }
}

不同的解法

public class Solution {
    public boolean wordBreak(String s, Set<String> dict) {

        boolean[] f = new boolean[s.length() + 1];

        f[0] = true;


        /* First DP for(int i = 1; i <= s.length(); i++){ for(String str: dict){ if(str.length() <= i){ if(f[i - str.length()]){ if(s.substring(i-str.length(), i).equals(str)){ f[i] = true; break; } } } } }*/

        //Second DP
        for(int i=1; i <= s.length(); i++){
            for(int j=0; j < i; j++){
                if(f[j] && dict.contains(s.substring(j, i))){
                    f[i] = true;
                    break;
                }
            }
        }

        return f[s.length()];
    }
}

140.Word Break II

题目链接:https://leetcode.com/problems/word-break-ii/description/

题目描述

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. You may assume the dictionary does not contain duplicate words.

Return all such possible sentences.

For example, given
s = “catsanddog”,
dict = [“cat”, “cats”, “and”, “sand”, “dog”].

A solution is [“cats and dog”, “cat sand dog”].

UPDATE (2017/1/4):
The wordDict parameter had been changed to a list of strings (instead of a set of strings). Please reload the code definition to get the latest changes.

算法设计

public class Solution {
    HashMap<Integer, List<String>> dp = new HashMap<>();

    public List<String> wordBreak(String s, Set<String> wordDict) {
        int maxLength = -1;
        for(String ss : wordDict) maxLength = Math.max(maxLength, ss.length());
        return addSpaces(s, wordDict, 0, maxLength);
    }

    private List<String> addSpaces(String s, Set<String> wordDict, int start, int max){
        List<String> words = new ArrayList<>();
        if(start == s.length()) {
            words.add("");
            return words;
        }
        for(int i = start + 1; i <= max + start && i <= s.length(); i++){
            String temp = s.substring(start, i);
            if(wordDict.contains(temp)){
                List<String> ll;
                if(dp.containsKey(i)) ll = dp.get(i);
                else ll = addSpaces(s, wordDict, i, max);
                for(String ss : ll) words.add(temp + (ss.equals("") ? "" : " ") + ss);
            }

        }
        dp.put(start, words);
        return words;
    }
}

(未完待续)

    原文作者:动态规划
    原文地址: https://blog.csdn.net/seagal890/article/details/79439201
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞