多维动态规划(5) --hot 100 in leetcode
不同路径
算法概述
原题
本题要求为统计二维矩阵中从左上角出发到右下角的所有可能。其实没区别的。
- 时间复杂度为O(mn):一次遍历
- 空间复杂度为O(n):经过优化
JAVA
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| class Solution { public int uniquePaths(int m, int n) { int[] cur = new int[n]; Arrays.fill(cur,1); for (int i = 1; i < m;i++){ for (int j = 1; j < n; j++){ cur[j] += cur[j-1] ; } } return cur[n-1]; } }
|
C++
注意
可以通过在 上一行遍历结果 的基础上操作将问题 降维 。
最小路径和
算法概述
原题
本题要求为在上题基础上加权值,返回最小路径和。
JAVA
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| class Solution { public int minPathSum(int[][] grid) { if (grid == null || grid.length == 0 || grid[0].length == 0) { return 0; } int rows = grid.length, columns = grid[0].length; int[][] dp = new int[rows][columns]; dp[0][0] = grid[0][0]; for (int i = 1; i < rows; i++) { dp[i][0] = dp[i - 1][0] + grid[i][0]; } for (int j = 1; j < columns; j++) { dp[0][j] = dp[0][j - 1] + grid[0][j]; } for (int i = 1; i < rows; i++) { for (int j = 1; j < columns; j++) { dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - 1]) + grid[i][j]; } } return dp[rows - 1][columns - 1]; } }
|
C++
注意
记得处理[0][0]
这种特殊的边界值。
最长回文子串
算法概述
原题
本题要求为找出给定字符串的最长回文子串。有很多种高级解法,而动态规划是基于 不断扩大回文子串长度 的。
- 时间复杂度为O(n^2)
- 空间复杂度为O(n^2)
JAVA
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| public class Solution {
public String longestPalindrome(String s) { int len = s.length(); if (len < 2) { return s; } int maxLen = 1; int begin = 0; boolean[][] dp = new boolean[len][len]; for (int i = 0; i < len; i++) { dp[i][i] = true; } char[] charArray = s.toCharArray(); for (int L = 2; L <= len; L++) { for (int i = 0; i < len; i++) { int j = L + i - 1; if (j >= len) { break; } if (charArray[i] != charArray[j]) { dp[i][j] = false; } else { if (j - i < 3) { dp[i][j] = true; } else { dp[i][j] = dp[i + 1][j - 1]; } }
if (dp[i][j] && j - i + 1 > maxLen) { maxLen = j - i + 1; begin = i; } } } return s.substring(begin, begin + maxLen); } }
|
C++
注意
这道题的思路 不太一样 ,它 把一维上的双指针映射成二维形式 。这样做的好处就是对于动态规划来讲,易于处理。状态转移方程也很不一样,子问题结构 并不是之前的正序和倒序 ,而是 内部窗口的双指针 。这样做就将状态转移方程的更新条件简化为 两边的新字符一不一样 的问题。
较为特别,思路打开,需要多加练习熟练掌握
最长公共子序列
算法概述
原题
本题要求如题所示,子序列指的是可以删除一些元素但不可移动元素位置。像上题一样,使用 二维映射指针 。
JAVA
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| class Solution { public int longestCommonSubsequence(String text1, String text2) { int m = text1.length(), n = text2.length(); int[][] dp = new int[m + 1][n + 1]; for (int i = 1; i <= m; i++) { char c1 = text1.charAt(i - 1); for (int j = 1; j <= n; j++) { char c2 = text2.charAt(j - 1); if (c1 == c2) { dp[i][j] = dp[i - 1][j - 1] + 1; } else { dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]); } } } return dp[m][n]; } }
|
C++
注意
其实没什么特别的,就是双层遍历,像int[][] dp = new int[m + 1][n + 1];
这种技巧需要掌握。
编辑距离
算法概述
原题
本题要求为返回将一个字符串转换成另外一个字符串的最少操作数(操作包括:插入、删除、替换)。
JAVA
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| class Solution { public int minDistance(String word1, String word2) { int n = word1.length(); int m = word2.length();
if (n * m == 0) { return n + m; }
int[][] D = new int[n + 1][m + 1];
for (int i = 0; i < n + 1; i++) { D[i][0] = i; } for (int j = 0; j < m + 1; j++) { D[0][j] = j; }
for (int i = 1; i < n + 1; i++) { for (int j = 1; j < m + 1; j++) { int left = D[i - 1][j] + 1; int down = D[i][j - 1] + 1; int left_down = D[i - 1][j - 1]; if (word1.charAt(i - 1) != word2.charAt(j - 1)) { left_down += 1; } D[i][j] = Math.min(left, Math.min(down, left_down)); } } return D[n][m]; } }
|
C++
注意
看似是列举了所有可能,但其实还是优化了,切记 不要混淆了操作和子问题 ,left
、down
这些只是解决方法,不是子问题的最终答案,核心还是通过Math.min(left, Math.min(down, left_down));
这个状态方程解决每个子问题的。