博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
算法刷题:LeetCode中常见的动态规划题目
阅读量:4041 次
发布时间:2019-05-24

本文共 15930 字,大约阅读时间需要 53 分钟。

动态规划刷题笔记

53. Maximum Subarray

原题链接地址:

题目描述

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

85.Maximal Rectangle

题目链接:

题目描述

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

题目链接:

题目描述

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

原题链接地址:

题目描述

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
> &triangle) { int n = triangle.size(); vector
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
> triangle) { if(triangle.size() == 0) return 0; for (int i=triangle.size() - 2; i>=0; i--) { for (int j=0; j<=i; j++) { List
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

题目链接:

题目描述

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
& prices) { if (prices.size() < 2) return 0; const int n = prices.size(); vector
f(n, 0); vector
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

题目链接:

64.Minimum Path Sum

题目链接:

72.Edit Distance

题目链接:

91.Decode Ways

题目链接:

639.Decode Ways II

题目链接:

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

题目链接:

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

题目链接:

题目描述

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
> dp = new HashMap<>(); public List
wordBreak(String s, Set
wordDict) { int maxLength = -1; for(String ss : wordDict) maxLength = Math.max(maxLength, ss.length()); return addSpaces(s, wordDict, 0, maxLength); } private List
addSpaces(String s, Set
wordDict, int start, int max){ List
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
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; }}

(未完待续)

转载地址:http://xgvdi.baihongyu.com/

你可能感兴趣的文章
剑指offer算法题分析与整理(三)
查看>>
Ubuntu 13.10使用fcitx输入法
查看>>
pidgin-lwqq 安装
查看>>
mint/ubuntu安装搜狗输入法
查看>>
C++动态申请数组和参数传递问题
查看>>
opencv学习——在MFC中读取和显示图像
查看>>
retext出现Could not parse file contents, check if you have the necessary module installed解决方案
查看>>
Matlab与CUDA C的混合编程配置出现的问题及解决方案
查看>>
python一句话之利用文件对话框获取文件路径
查看>>
PaperDownloader——文献命名6起来
查看>>
如何将PaperDownloader下载的文献存放到任意位置
查看>>
C/C++中关于动态生成一维数组和二维数组的学习
查看>>
JVM最简生存指南
查看>>
Java的对象驻留
查看>>
logback高级特性使用(二) 自定义Pattern模板
查看>>
JVM并发机制探讨—内存模型、内存可见性和指令重排序
查看>>
可扩展、高可用服务网络设计方案
查看>>
如何构建高扩展性网站
查看>>
微服务架构的设计模式
查看>>
持续可用与CAP理论 – 一个系统开发者的观点
查看>>