Leetcode动态规划题答案

动态规划一直是比较难的题目,在刷leetcode时候,遇到的动态规划不下于10道题,现在挑出来一些给大家分享下,希望大家在接下来的秋招有个好的收成!

DP就是时间换空间,将一个递归过程转换成表,往往是二维的表,主要是如何变才是重点。

leetcode的第32题:最长有效括号,将真不看答案我死的没想到可以用动态规划的,我是递归实现,可惜超时了。。

import java.util.Stack;

public class 动态规划最长有效的括号_32 {
	public static void main(String[] args) {
		动态规划最长有效的括号_32 test = new 动态规划最长有效的括号_32();
		int longestValidParentheses = test
				.longestValidParentheses(")((()))()(())))");
		System.out.println(longestValidParentheses);
	}

	public int longestValidParentheses(String s) {
		Stack<Integer> stack = new Stack<Integer>();
		int[] S = new int[s.length()];
		for (int i = 0; i < s.length(); i++) {
			if (s.charAt(i) == '(') {
				stack.push(i);
			} else {
				if (!stack.isEmpty()) {
					S[i] = 1;
					S[stack.pop()] = 1;
				}
			}
		}
		int maxLength = 0;
		int curLength = 0;
		// 找到最长怜恤1子串
		for (int i = 0; i < S.length; i++) {
			if (S[i] == 1) {
				curLength++;
			} else {
				curLength = 0;
			}
			if (curLength > maxLength) {
				maxLength = curLength;
			}
		}
		return maxLength;
	}
	
	
    public int longestValidParentheses2(String s) {
    	//以i开始到字符串结束的最长有效括号长度
        int[] dp = new int[s.length()];
        int maxLen = 0;
        for(int i = s.length()-2; i >=0; i--){
        	//s[i]是字符串下标为i的括号
            if(s.charAt(i)=='('){
            	//如果s[i-1]是左括号,如果i + d[i] + 1是右括号的话,那d[i-1] = d[i] + 2
                int end = i + dp[i+1] + 1;
                if(end < s.length() && s.charAt(end)==')'){
                    dp[i] = dp[i+1] + 2;
                    //如果不是则为0。如果s[i-1]是右括号,因为不可能有右括号开头的括号对,所以d[i-1] = 0。
                    if(end + 1 < s.length()){
                        dp[i] += dp[end + 1];
                    }
                }
            }
            maxLen = Math.max(maxLen, dp[i]);
        }
        return maxLen;
    }
}

leetcode的第53题:最大连续子序列

这道题看各种书上都有介绍,没什么好说的,如果不会说明你压根一本面试书都没看

public class 动态规划之最大连续和的子序列_53 {
	public int maxSubArray(int[] nums) {
		int nEnd=nums[0];
		int nAll=nums[0];
		for (int i = 1; i < nums.length; i++) {
			nEnd=Math.max(nEnd+nums[i], nums[i]);
			nAll=Math.max(nAll, nEnd);
		}
		return nAll;
	}
}

leetcode的第62-63-64题,棋盘上的路径问题:这个比较好理解,就是左边,上边的当前位置的影响而已:

public class 动态规划之棋盘上所有不同路径_62 {
	 public int uniquePaths(int m, int n) {
		 int [][]dp=new int [m][n];
		 for (int i = 0; i < m; i++) {
			dp[i][0]=1;
		 }
		 for (int i = 0; i < n; i++) {
			dp[0][i]=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];
	 }
}

public class 动态规划之迷宫中能走的所有不同路径_63 {
	public static void main(String[] args) {
		动态规划之迷宫中能走的所有不同路径_63 test = new 动态规划之迷宫中能走的所有不同路径_63();
		int[][] obstacleGrid = new int[][] { { 0, 1 } };
		int uniquePathsWithObstacles = test
				.uniquePathsWithObstacles(obstacleGrid);
		System.out.println(uniquePathsWithObstacles);
	}

	public int uniquePathsWithObstacles(int[][] obstacleGrid) {
		if (obstacleGrid == null) {
			return 0;
		}
		int m = obstacleGrid.length;
		int n = obstacleGrid[0].length;
		int[][] dp = new int[m][n];
		if (obstacleGrid[0][0] == 1) {
			return 0;
		}
		dp[0][0] = 1;
		for (int i = 1; i < m; i++) {
			if (obstacleGrid[i][0] == 0) {
				dp[i][0] = dp[i - 1][0];
			}
		}
		for (int i = 1; i < n; i++) {
			if (obstacleGrid[0][i] == 0) {
				dp[0][i] = dp[0][i - 1];
			}
		}
		for (int i = 1; i < m; i++) {
			for (int j = 1; j < n; j++) {
				if (obstacleGrid[i][j] == 0) {
					dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
				}
			}
		}
		return dp[m - 1][n - 1];
	}
}

public class 动态规划之棋盘上路径所有元素和最小值_64 {
	public int minPathSum(int[][] grid) {
		if (grid == null) {
			return 0;
		}
		int m = grid.length;
		int n = grid[0].length;
		int[][] dp = new int[m][n];
		dp[0][0] = grid[0][0];
		for (int i = 1; i < m; i++) {
			dp[i][0] = grid[i][0] + dp[i - 1][0];
		}
		for (int i = 1; i < n; i++) {
			dp[0][i] = grid[0][i] + dp[0][i - 1];
		}
		for (int i = 1; i < m; i++) {
			for (int j = 1; j < n; j++) {
				dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - 1]) + grid[i][j];
			}
		}
		return dp[m - 1][n - 1];
	}
}

leetcode的72题:字符串的替换,删除,插入距离代价,其实这一题可以改编成替换代价,删除代价和插入代价不等。

public class 动态规划之计算字符串的距离_72 {
	  public int minDistance(String word1, String word2) {
		int n=word1.length();
		int m=word2.length();
		//A中前i个元素转到B中前j个元素所需要的最小代价
		int [][]dp=new int [n+1][m+1];
		//表示A前i个元素转为B前0个元素的操作是需要i次的
		for (int i = 0; i <=n; i++) {
			dp[i][0]=i;
		}
		//表示A中前0个元素转为B的前i个元素的操作次数是i次
		for (int i = 0; i <=m; i++) {
			dp[0][i]=i;
		}
		for (int i = 1; i <=n; i++) {
			for (int j = 1; j <=m; j++) {
				if (word1.charAt(i-1)==word2.charAt(j-1)) {
					dp[i][j]=dp[i-1][j-1];
				}else{
					//dp[i-1][j-1]+1代表是替换一个元素,
					//dp[i][j-1]+1代表是添加一个元素,
					//dp[i-1][j]+1代表是删除一个元素
					dp[i][j]=Math.min(Math.min(dp[i-1][j]+1,dp[i][j-1]+1),dp[i-1][j-1]+1);
				}
			}
		}
		return dp[n][m];
	  }
}

leetcode:第91题解码方式,当时我对这道题有点懵逼,为什么只判断开头不为0,中间有0怎么办啊,,哎说多都是泪,算法真难学:

public class 动态规划之解码方式_91 {
	public static void main(String[] args) {
		动态规划之解码方式_91 test=new 动态规划之解码方式_91();
		int numDecodings = test.numDecodings("12121");
		System.out.println(numDecodings);
	}
	
	  public int numDecodings(String s) {
		 if (s.length()==0||s.startsWith("0")) {
			return 0;
		 }
		 int len=s.length()+1;
		 int [] dp=new int[len];
		 dp[0]=1;
		 dp[1]=1;
		 for (int i = 2; i < len; i++) {
			int t=Integer.parseInt(s.substring(i-2,i));
			if (10<=t&&t<=26) {
				dp[i]+=dp[i-2];
			}
			int temp=Integer.parseInt(s.substring(i-1,i));
			if (1<=temp&&temp<=9) {
				dp[i]+=dp[i-1];
			}
		 }
		 return dp[len-1];
	   }
}

leetcode:第96题二叉树的生成方式:

import java.util.ArrayList;
import java.util.List;

public class 动态规划之二叉树生成总数_96 {
	public static void main(String[] args) {
		动态规划之二叉树生成总数_96 test = new 动态规划之二叉树生成总数_96();
		// List<TreeNode> generateTrees = test.generateTrees(3);
		int nums = test.generateTreesCount(2);
		System.out.println(nums);
	}

	// BST树:以i为根节点的树,其左子树由[0, i-1]构成, 其右子树由[i+1, n]构成。
	// 动态规划状态为count[n],count[n]表示到正整数i为止的BST个数 ;
	private int generateTreesCount(int n) {
		int[] dp = new int[n + 1];
		dp[0] = 1;// 表示为空节点,情况只有一种
		for (int i = 1; i <= n; i++) {// 表示以i为根节点
			for (int j = 0; j < i; j++) {// j表示左字数的构造种类,i-j-1表示右字数构造种类
				dp[i] += dp[j] * dp[i - j - 1];// 左字数种类*右字数种类
			}
		}
		return dp[n];
	}

	public List<TreeNode> generateTrees(int n) {
		if (n<1) {
			return new ArrayList<>();
		}
		return getnereteTree(1, n);
	}

	private List<TreeNode> getnereteTree(int left, int right) {
		List<TreeNode> list = new ArrayList<>();
		if (left > right) {
			list.add(null);
			return list;
		}
		for (int i = left; i <= right; i++) {
			List<TreeNode> lefts = getnereteTree(left, i - 1);// 以i作为根节点,左子树由[1,i-1]构成
			List<TreeNode> rights = getnereteTree(i + 1, right);// 右子树由[i+1, n]构成
			for (int j = 0; j < lefts.size(); j++) {
				for (int k = 0; k < rights.size(); k++) {
					TreeNode root = new TreeNode(i);
					root.left = lefts.get(j);
					root.right = rights.get(k);
					list.add(root);// 存储所有可能行
				}
			}
		}
		return list;
	}

}

点赞