多维动态规划
文章目录
-
- 多维动态规划
-
- [62. 不同路径](#62. 不同路径)
- [64. 最小路径和](#64. 最小路径和)
- [5. 最长回文子串](#5. 最长回文子串)
- [1143. 最长公共子序列](#1143. 最长公共子序列)
- [72. 编辑距离](#72. 编辑距离)
62. 不同路径
d f s ( i , j ) = d f s ( i − 1 , j ) + d f s ( i , j − 1 ) dfs(i,j)=dfs(i−1,j)+dfs(i,j−1) dfs(i,j)=dfs(i−1,j)+dfs(i,j−1)
到达 (i, j) 的路径数 = 从上方来的路径数 + 从左方来的路径数
递归函数 dfs(i, j)含义:从 (0,0) 到 (i,j) 有多少条路径
java
class Solution {
public int uniquePaths(int m, int n) {
int[][] memo = new int[m][n];
return dfs(m-1,n-1,memo);
}
private int dfs(int i,int j,int[][] memo){
if(i<0||j<0){//越界了没有路径,返回0
return 0;
}
if(i==0&&j==0){//到达起点只有一条路径
return 1;
}
if(memo[i][j] != 0){
return memo[i][j];//这个位置已经算过了,直接返回缓存结果,避免重复计算
}
return memo[i][j] = dfs(i-1,j,memo) + dfs(i,j-1,memo);
}
}
64. 最小路径和
d f s ( i , j ) = m i n ( d f s ( i , j − 1 ) , d f s ( i − 1 , j ) ) + g r i d i j dfs(i,j)=min(dfs(i,j−1),dfs(i−1,j))+gridij dfs(i,j)=min(dfs(i,j−1),dfs(i−1,j))+gridij
从终点 (m-1, n-1) 往回推 到起点 (0, 0),每一步只能从上方 或左方过来,取代价更小的那条路。
逐步拆解
- 入口 :从终点
(m-1, n-1)开始递归 - 越界 :
i < 0 || j < 0,返回Integer.MAX_VALUE,表示这条路走不通 - 起点 :
i == 0 && j == 0,返回grid[0][0],递归终止 - 查缓存 :
memo[i][j] != 0,说明算过了,直接返回,避免重复计算 - 状态转移 :
memo[i][j] = Math.min(dfs(i-1,j), dfs(i,j-1)) + grid[i][j];
java
class Solution {
public int minPathSum(int[][] grid) {
int m = grid.length;
int n = grid[0].length;
int[][] memo = new int[m][n];
return dfs(m-1,n-1,grid,memo);
}
private int dfs(int i,int j,int[][] grid,int[][]memo){
if(i < 0 || j < 0){
return Integer.MAX_VALUE;
}
if(i == 0 && j == 0){
return grid[i][j];
}
if(memo[i][j] != 0){
return memo[i][j];
}
return memo[i][j] = Math.min(dfs(i-1,j,grid,memo),dfs(i,j-1,grid,memo)) + grid[i][j];
}
}
5. 最长回文子串
中心扩展法
- c 是回文串
- 看看 c 左右两边的字母是不是一样的,一样,那么 bcb 是回文串
- 继续,看看 bcb 左右两边的字母是不是一样的,一样,那么 abcba 是回文串。我们 O(1) 地判断出了一个子串是不是回文串

java
class Solution {
public String longestPalindrome(String s) {
char[] S = s.toCharArray();
int n = S.length;
int ansL = 0;
int ansR = 0;//左闭右开,因为substring左闭右开
//奇回文串
for(int i = 0;i < n;i++){
int l = i;
int r = i;
while(l >=0 && r < n && S[l] == S[r]){
l--;
r++;
}
//循环结束后,s[l+1]到s[r-1]是回文串
if(r - l - 1 > ansR - ansL){
ansL = l + 1;
ansR = r;//左闭右开
}
}
//偶回文串
for(int i = 0;i < n - 1;i++){
int l = i;
int r = i + 1;
while(l >= 0 && r < n && S[l] == S[r]){
l--;
r++;
}
if(r - l - 1 > ansR - ansL){
ansL = l + 1;
ansR = r;
}
}
return s.substring(ansL,ansR);
}
}
1143. 最长公共子序列
这道题求两个字符串的最长公共子序列(LCS)。
核心思路是:从两个字符串的末尾往前比,每个位置只有两种情况:
- 字符相同 :这个字符一定在 LCS 里,直接
+1,然后两个指针都往前移 - 字符不同 :要么跳过
s的当前字符,要么跳过t的当前字符,取两种选择中 LCS 更长的
java
class Solution {
private char[] s, t;
private int[][] memo;
public int longestCommonSubsequence(String text1, String text2) {
s = text1.toCharArray();
t = text2.toCharArray();
int n = s.length;
int m = t.length;
memo = new int[n][m];
for (int[] row : memo) {
Arrays.fill(row, -1); // -1 表示没有计算过
}
return dfs(n - 1, m - 1);
}
private int dfs(int i, int j) {
if (i < 0 || j < 0) {
return 0;
}
if (memo[i][j] != -1) {
return memo[i][j]; // 之前计算过
}
if (s[i] == t[j]) {
return memo[i][j] = dfs(i - 1, j - 1) + 1;//继续往前一位比较
}
return memo[i][j] = Math.max(dfs(i - 1, j), dfs(i, j - 1));//跳过s或t的字符
}
}
时间复杂度较高,还需优化
72. 编辑距离
还是从两个字符串末尾往前比,dfs(i, j) 的含义是:把 s[0..i] 变成 t[0..j] 最少需要几步
边界条件:
i < 0:s已经空了,但t还剩j+1个字符没匹配,只能全部插入 ,所以需要j+1步j < 0:t已经空了,但s还剩i+1个字符没处理,只能全部删除 ,所以需要i+1步
字符相同:
java
if (s[i] == t[j]) {
return memo[i][j] = dfs(i - 1, j - 1);
}
当前字符一样,不需要任何操作,直接两个指针都往前移。
字符不同:
java
return memo[i][j] = Math.min(
Math.min(dfs(i - 1, j), // 删除 s[i]
dfs(i, j - 1)), // 插入 t[j]
dfs(i - 1, j - 1) // 替换 s[i] 为 t[j]
) + 1;
| 操作 | 含义 | 指针变化 |
|---|---|---|
dfs(i-1, j) |
删除 s[i],s 指针前移,t 不动 |
i-1, j |
dfs(i, j-1) |
在 s 中插入 t[j],t 指针前移,s 不动 |
i, j-1 |
dfs(i-1, j-1) |
把 s[i] 替换成 t[j],两个指针都前移 |
i-1, j-1 |
三种操作都花了1步,所以最后+1
java
class Solution {
private char[] s,t;
private int[][] memo;
public int minDistance(String word1, String word2) {
s = word1.toCharArray();
t = word2.toCharArray();
int n = s.length;
int m = t.length;
memo = new int[n][m];
for(int[] row : memo){
Arrays.fill(row,-1);
}
return dfs(n-1,m-1);
}
private int dfs(int i,int j){
if(i < 0){
return j + 1;
}
if(j < 0){
return i + 1;
}
if(memo[i][j] != -1){
return memo[i][j];
}
if(s[i] == t[j]){
return memo[i][j] = dfs(i-1,j-1);
}
return memo[i][j] = Math.min(Math.min(dfs(i-1,j),dfs(i,j-1)),dfs(i-1,j-1)) + 1;
}
}