# LeetCode：动态规划+贪心题目整理

## 1.字符串分割：（LeetCode：word-break）

Given a string s and a dictionary of words dict, determine if s can be segmented into a space-separated sequence of one or more dictionary words.

For example, given  s ="leetcode",    dict =["leet", "code"]. Return true because"leetcode"can be segmented as"leet code".

题意：判断字符串能够分割为有集合中的几个词汇组成。

题解： 动态规划，状态visited(i)：表示以i结尾的是否能够满足条件，每次对从0-i的字符进行判断。

import java.util.*;
public class Solution {
public boolean wordBreak(String s, Set<String> dict) {
char [] list = s.toCharArray();
boolean [] visited = new boolean[list.length+1];
visited[0] = true;
for(int i =1;i<=list.length;i++){
for(int j = 0 ;j< i;j++){
if(visited[j] && dict.contains(s.substring(j,i))){
visited[i] = true;
break;
}
}
}
return visited[list.length];
}
}

## 2.字符串分割II（LeetCode：word-break-ii）

Given a string s and a dictionary of words dict, add spaces in s to construct a sentence where each word is a valid dictionary word.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"].

题意：字符串分割后组成的句子的所有组合。

题解：使用DFS，从头部开始会超时，因此，从尾部开始，进行DFS，最后添加时去掉尾部的空格。

import java.util.*;
public class Solution {
ArrayList<String> result = new ArrayList<String>();
public void dfs(String s,int index, Set<String> dict,String cur) {
if(index<=0){
if(cur.length()>0)
}
for(int i = index;i>=0;i--){
if(dict.contains(s.substring(i,index))){
dfs(s,i,dict,s.substring(i,index)+" "+cur);
}
}
}

public ArrayList<String> wordBreak(String s, Set<String> dict) {
dfs(s,s.length(),dict,"");
return result;
}
}

## 3.分糖果（LeetCode：candy）：

There are N children standing in a line. Each child is assigned a rating value.You are giving candies to these children subjected to the following requirements:

• Each child must have at least one candy.
• Children with a higher rating get more candies than their neighbors.

What is the minimum candies you must give?

题意：每个小朋友都有一颗糖，节点高的同学比旁边的孩子多拿糖，求最少的糖个数。

题解：遍历两边，首先每个人得一块糖，第一遍从左到右，若当前点比前一个点高就比前者多一块。 这样保证了在一个方向上满足了要求。第二遍从右往左，若左右两点，左侧高于右侧，但左侧的糖果数不多于右侧，则左侧糖果数等于右侧糖果数+1，这就保证了另一个方向上满足要求

public class Solution {
public int candy(int[] ratings) {
int dp[] = new int[ratings.length];
if(ratings.length==1)
return 1;
for(int i =0;i<ratings.length;i++){
dp[i]= 1;
}
for(int i =1;i<ratings.length;i++){
if(ratings[i]>ratings[i-1]  )
dp[i]= dp[i-1]+1;
}
int count =dp[ratings.length-1];
for(int i =ratings.length-2;i>=0;i--){
if(ratings[i]>ratings[i+1] && dp[i]<=dp[i+1] )
dp[i]= dp[i+1]+1;
count+=dp[i];
}
return count;
}
}

## 4.加油站（LeetCode：gas-station）

There are N gas stations along a circular route, where the amount of gas at station i isgas[i]. You have a car with an unlimited gas tank and it costscost[i]of gas to travel from station i to its next station (i+1). You begin the journey with an empty tank at one of the gas stations. Return the starting gas station's index if you can travel around the circuit once, otherwise return -1.

Note:
The solution is guaranteed to be unique.

题意：环形路线上有N个加油站，每个加油站有汽油gas[i]，从每个加油站到下一站消耗汽油cost[i]，问从哪个加油站出发能够回到起始点，如果都不能则返回-1（注意，解是唯一的）。

题解：total用来判断是否有解，total<0表示没有解，同理，从i点出发，如果remains<0，表示无法到达该点，应该从下一个点开始重新计算，因为第一个点开始时remains>0，当小于0时，中间的点也无法到达该点。

public class Solution {
public int canCompleteCircuit(int[] gas, int[] cost) {
int index = -1;
int total =0;
int dif =0;
int remains = 0;
for(int i=0;i<gas.length;i++){
dif = gas[i]-cost[i];
remains +=dif;
total +=dif;
if(remains<0){
index = i;
remains=0;
}
}
if(total<0)
return -1;
else
return index+1;
}
}

## 5.三角形求最小路径和（LeetCode：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 is11(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.

题解：自底向上，先将最后一层的数字赋值给dp数组，然后自底向上，每一层的节点和下一层的两个节点进行比较，得到最小值，最后输出dp[0]，即为最小值。

import java.util.*;
public class Solution {
public int minimumTotal(ArrayList<ArrayList<Integer>> triangle) {
ArrayList<Integer> list = triangle.get(triangle.size()-1);
int len = list.size();
int dp[] = new int[len];
for(int i =0;i<len;i++){
dp[i] = list.get(i);
}
for(int i =triangle.size()-2;i>=0;i--){
int temp = triangle.get(i).size();
for(int j =0;j<temp;j++){
dp[j] = Math.min(triangle.get(i).get(j)+ dp[j+1]
,triangle.get(i).get(j)+ dp[j]);
}
}
return dp[0];
}
}

## 6.不同子序列（LeetCode：distinct-subsequences）

Given a string S and a string T, count the number of distinct subsequences of T in S. A subsequence of a string is a new string which is formed from the original string by deleting some (can be none) of the characters without disturbing the relative positions of the remaining characters. (ie,"ACE"is a subsequence of"ABCDE"while"AEC"is not).

Here is an example:   S ="rabbbit", T ="rabbit"    Return3.

题意：给定两个字符串，选择只可以用删除字符的方法从第一个字符串变换到第二个字符串，求出一共有多少种变换方法；

题解：定义二维数组dp[i][j]为字符串s(0,i)变换到t(0,j)的变换方法。如果S[i]==T[j]，那么dp[i][j] = dp[i-1][j-1] + dp[i-1][j]。意思是：如果当前S[i]==T[j]，那么当前这个字母即可以保留也可以抛弃。如果S[i]!=T[i]，那么dp[i][j] = dp[i-1][j]，意思是如果当前字符不等，那么就只能抛弃当前这个字符。递归公式中用到的dp[0][0] = 1，dp[i][0] = 0（把任意一个字符串变换为一个空串只有一个方法）

public class Solution {
public int numDistinct(String S, String T) {
int n = S.length();
int m = T.length();
int [][]dp = new int[m+1][n+1];
dp[0][0]=1;
for(int i =1;i<=m;i++){
dp[i][0] = 0;
}
for(int j =1;j<=n;j++){
dp[0][j] = 1;
}
for(int i =1;i<=m;i++){
for(int j =1;j<=n;j++){
if(S.charAt(j-1)== T.charAt(i-1))
dp[i][j] = dp[i-1][j-1]+dp[i][j-1];
else
dp[i][j] = dp[i][j-1];
}
}
return dp[m][n];
}
}

## 7.判断字符串是否由两个子串拼接而成：（LeetCode：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",

题意：s3是由s1和s2交织生成的，意味着s3由s1和s2组成，在s3中s1和s2字符的顺序是不能变化的，和子序列题型类似，这种题我们一般是用动态规划来解。

题解： 设dp[i][j]表示s3的前i+j个字符可以由s1的前i个字符和s2的前j个字符交织而成。状态转移方程：有两种情况 ：

dp[i][j]= {(dp[i - 1][j] && s1.charAt(i - 1) == s3.charAt(i + j - 1)}

dp[i][j]= {(dp[i][j-1] && s2.charAt(j - 1) == s3.charAt(i + j - 1)}

分别表示第i+j个字符由s1构成或者由S2组成。

public class Solution {
public boolean isInterleave(String s1, String s2, String s3) {
if(s1.length() + s2.length() != s3.length())
return false;
if(s1==null )
return s2.equals(s3);
if(s2==null)
return s1.equals(s3);
//dp[i][j]代表 s1[0...i]  s2[0...j]能否顺序匹配 s3[i+j-1]
boolean dp[][] = new boolean[s1.length()+1][s2.length()+1];
dp[0][0] = true;
//初始化边界值
for(int i =1;i<=s1.length();i++){
dp[i][0] = dp[i-1][0] && s1.charAt(i-1)==s3.charAt(i-1);
}
for(int j =1;j<=s2.length();j++){
dp[0][j] = dp[0][j-1] && s2.charAt(j-1)==s3.charAt(j-1);
}
for(int i =1;i<=s1.length();i++){
for(int j =1;j<=s2.length();j++){
dp[i][j] = (dp[i][j-1] && s2.charAt(j-1)==s3.charAt(i+j-1))
|| (dp[i-1][j] && s1.charAt(i-1)==s3.charAt(i+j-1));
}
}
return dp[s1.length()][s2.length()];
}
}

## 8.  数字转字符串（LeetCode：decode-ways）

A message containing letters fromA-Zis being encoded to numbers using the following mapping:

'A' -> 1
'B' -> 2
...
'Z' -> 26


Given an encoded message containing digits, determine the total number of ways to decode it. For example, Given encoded message"12", it could be decoded as"AB"(1 2) or"L"(12). The number of ways decoding"12"is 2.

题解：动态规划。类似于剑指offer中的数字转字符串，区别在于从0开始和从1开始。

public class Solution {
public int numDecodings(String s) {
char list[] = s.toCharArray();
if(list.length==0 || list[0]=='0')
return 0;
if(list.length==1)
return 1;
int [] dp = new int[list.length+1];
dp[0]=1;
if(list[0]=='0')
dp[1]=0;
else
dp[1]=1;
for(int i = 2;i<=list.length;i++){
if((list[i-1]-'0')!=0)
dp[i] = dp[i-1];
else
dp[i] = 0;
if((list[i-2]-'0')==1 ||
((list[i-2]-'0')==2 && (list[i-1]-'0')<=6))
dp[i] += dp[i-2];

}
return dp[list.length];
}
}

## 9.  格雷码（LeetCode：gray-code）

The gray code is a binary numeral system where two successive values differ in only one bit.Given a non-negative integer n representing the total number of bits in the code, print the sequence of gray code. A gray code sequence must begin with 0.For example, given n = 2, return[0,1,3,2]. Its gray code sequence is:

00 - 0
01 - 1
11 - 3
10 - 2


Note:
For a given n, a gray code sequence is not uniquely defined.For example,[0,2,3,1]is also a valid gray code sequence according to the above definition.For now, the judge is able to judge based on one instance of gray code sequence. Sorry about that.

题解：随着n变大，前面的数不用动，后面的数倒着拿出来再在首部加1即可。

import java.util.*;
public class Solution {
ArrayList<Integer> result =  new ArrayList<Integer>();
public ArrayList<Integer> grayCode(int n) {
if(n==0)
return result;
if(n==1)
return result;
for(int i =2;i<=n;i++){
int size = result.size();
for(int j =size-1;j>=0;j--){
}
}
return result;
}
}

## 10.直方图中最大的矩形（LeetCode：largest-rectangle-in-histogram）

Given n non-negative integers representing the histogram's bar height where the width of each bar is 1, find the area of largest rectangle in the histogram.

Above is a histogram where width of each bar is 1, given height =[2,1,5,6,2,3].

The largest rectangle is shown in the shaded area, which has area =10unit.

For example,Given height =[2,1,5,6,2,3],return10.

题解：利用单调栈来实现，栈中保存的是索引的位置，满足递增时，将元素压入栈中，不满足时依次弹出，直到满足，弹出的过程计算以弹出元素为高度时的最大面积。

import java.util.*;
public class Solution {
public int largestRectangleArea(int[] height) {
if(height.length==1)
return height[0];
Stack<Integer> s = new Stack<Integer>();
int cur = 0;
int max = 0;
int len =0;
for(int i =0;i<height.length;i++){
if(s.isEmpty() || height[s.peek()]<=height[i]){
s.push(i);
}else{
while(!s.isEmpty() && height[s.peek()]>height[i]){
int index = s.pop();
if(s.isEmpty())//删除一个元素后为空
len = i;
else
len = i-s.peek()-1;
max = Math.max(max,len*height[index]);
}
s.push(i);
}
}
while(!s.isEmpty()){
int index = s.pop();
if(s.isEmpty())//删除一个元素后为空
len = height.length;
else
len = height.length-s.peek()-1;
max = Math.max(max,len*height[index]);
}
return max;
}
}

## 11.矩阵中的最大全1矩形面积：（LeetCode：maximal-rectangle）

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

题解：将矩形按照列进行统计转化，然后将每一行调用上一题中的计算直方图中的最大面积方法，得到最大值，最后找到整体的最大值。

import java.util.*;
public class Solution {
public int maximalRectangle(char[][] matrix) {
if(matrix.length==0 || matrix==null)
return 0;
int m = matrix.length;
int n = matrix[0].length;
int [][] temp = new int[m][n];
int max =0;
int result =0;
for(int j =0;j<n;j++){
for(int i =0;i<m;i++){
if(matrix[i][j] == '1'){
if(i==0)
temp[i][j] = matrix[i][j]-'0';
else
temp[i][j] = temp[i-1][j] + 1;
}else{
temp[i][j] =0;
}
}
}
for(int i =0;i<m;i++){
result = Rectangle(temp[i]);
max = Math.max(max,result);
}
return max;
}

public int Rectangle(int[] temp) {
if(temp.length==1)
return temp[0];
int len =0;
int max =0;
Stack<Integer> s = new Stack<Integer>();
for(int k =0;k<temp.length;k++){
if(s.isEmpty() || temp[k]>=temp[s.peek()]){
s.push(k);
}else{
while(!s.isEmpty() && temp[k]<temp[s.peek()]){
int index = s.pop();
if(s.isEmpty())
len = k;
else
len = k-s.peek()-1;
max = Math.max(max,temp[index]*len);
}
s.push(k);
}
}
while(!s.isEmpty()){
int index = s.pop();
if(s.isEmpty())
len = temp.length;
else
len =temp.length-s.peek()-1;
max = Math.max(max,temp[index]*len);
}
return max;
}

}

## 12.编辑距离（LeetCode：edit-distance）

Given two words word1 and word2, find the minimum number of steps required to convert word1 to word2. (each operation is counted as 1 step.)You have the following 3 operations permitted on a word:

a) Insert a character
b) Delete a character
c) Replace a character

题意：从一个字符串转变到另一个字符串需要的变换步骤，共有三种变换方式，插入一个字符，删除一个字符，和替换一个字符。

题解：设置二维动态数组，初始边界的值，循环遍历时，如果两个字符串所指的字符相等，则等于dp[i-1][j-1]，如果不相等，因为有三种操作，删除dp[i][j-1]、插入dp[i-1][j]、替换dp[i-1][j-1]，从中选择最小的值进行加1；

public class Solution {
public int minDistance(String word1, String word2) {
// dp[i][j]代表由word1的前i个子串变为word2的前j个子串的花费
int len1 = word1.length();
int len2 = word2.length();
int dp[][] = new int[len1+1][len2+1];
for(int i =0;i<=len1;i++){
dp[i][0] = i;
}
for(int j =0;j<=len2;j++){
dp[0][j] = j;
}
for(int i =1;i<=len1;i++){
for(int j =1;j<=len2;j++){
if(word1.charAt(i-1)==word2.charAt(j-1))
dp[i][j] = dp[i-1][j-1];
else
dp[i][j] =  Math.min(dp[i-1][j-1],
Math.min(dp[i][j-1],dp[i-1][j]))+1;
}
}
return dp[len1][len2];
}
}

## 13.最小路径和（LeetCode：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.

题意：从矩阵的左上角到右下角的最小路径和。

题解：设置二维动态数组来保存当前点的最小值，对每一行及每一列进行初始化，然后开始遍历，最小值为左和上的最小值+当前值。

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

## 14.机器人走方格（LeetCode：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?

题意：每次只能向下或者向右走，求路线总数。

题解：设置二维动态数组，初始边界的值，为1，然后循环判断每个点的路线数，当前点的路线数=左边点的路线数+上边点的路线数。

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

## 15.机器人走方格（LeetCode：unique-paths-ii）：

题意：方格中考虑障碍， 每次只能向下或者向右走，求路线总数。

题解：设置二维动态数组，初始边界的值，如果没有障碍，设为1，然后循环判断每个点的路线数，没有障碍时，当前点的路线数=左边点的路线数+上边点的路线数。

public class Solution {
public int uniquePathsWithObstacles(int[][] obstacleGrid) {
int row = obstacleGrid.length;
int col = obstacleGrid[0].length;
int dp[][] = new int[row+1][col+1];
for(int i =0;i<row;i++){
if(obstacleGrid[i][0]==0)
dp[i][0]=1;
else
break;
}
for(int j =0;j<col;j++){
if(obstacleGrid[0][j]==0)
dp[0][j] = 1;
else
break;
}
for(int i =1;i<row;i++){
for(int j =1;j<col;j++){
if(obstacleGrid[i][j]==0)
dp[i][j] = dp[i-1][j] + dp[i][j-1];
else
dp[i][j] =0;
}
}
return dp[row-1][col-1];
}
}

## 16.跳跃问题（LeetCode：jump-game）

Given an array of non-negative integers, you are initially positioned at the first index of the array.Each element in the array represents your maximum jump length at that position.Determine if you are able to reach the last index.

For example:    A =[2,3,1,1,4], return   true.     A =[3,2,1,0,4], return   false.

题意：已知数组，值表示最大跳跃长度，求是否可以达到终点。

题解：采用贪心算法来解决，但需要保证该点可以到达，使用i<=max保证。

public class Solution {
public boolean canJump(int[] A) {
//贪心算法：
if(A.length==1)
return true;
int max=0;
//i<=max保证该点可以到达
for(int i =0;i<A.length && i<=max;i++){
int temp = A[i];
if(i+temp>max)
max=i+temp;
if(max>=A.length-1)
return true;
}
return false;
}
}

## 17.跳跃问题（LeetCode：jump-game-ii）

Given an array of non-negative integers, you are initially positioned at the first index of the array.Each element in the array represents your maximum jump length at that position.Your goal is to reach the last index in the minimum number of jumps.

For example:   Given array A =[2,3,1,1,4]。 The minimum number of jumps to reach the last index is2. (Jump1step from index 0 to 1, then3steps to the last index.)

题意：已知数组，值表示最大跳跃长度，求最少跳跃次数。

题解： 动态规划，dp[i]放置到达i点的最少步数，首先都赋值为最大，每一次进行比较，取最小值进行保存。

public class Solution {
public int jump(int[] A) {
int len = A.length;
int [] dp = new int[len+1];
for(int i =0;i<=len;i++)
dp[i] = Integer.MAX_VALUE;
dp[0]=0;
for(int i =0;i<len;i++){
int temp = A[i];
for(int j =0;j<=temp;j++){
if(i+j>len)
break;
dp[i+j] = Math.min(dp[i+j],dp[i]+1);
}
}
return dp[len-1];
}
}