【力扣hot100】多维动态规划|62、64、5、1143、72

多维动态规划

文章目录

    • 多维动态规划
      • [62. 不同路径](#62. 不同路径)
      • [64. 最小路径和](#64. 最小路径和)
      • [5. 最长回文子串](#5. 最长回文子串)
      • [1143. 最长公共子序列](#1143. 最长公共子序列)
      • [72. 编辑距离](#72. 编辑距离)

62. 不同路径

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. 最小路径和

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. 最长回文子串

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. 最长公共子序列

1143. 最长公共子序列

这道题求两个字符串的最长公共子序列(LCS)

核心思路是:从两个字符串的末尾往前比,每个位置只有两种情况:

  1. 字符相同 :这个字符一定在 LCS 里,直接 +1,然后两个指针都往前移
  2. 字符不同 :要么跳过 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. 编辑距离

72. 编辑距离

还是从两个字符串末尾往前比,dfs(i, j) 的含义是:把 s[0..i] 变成 t[0..j] 最少需要几步

边界条件

  • i < 0s 已经空了,但 t 还剩 j+1 个字符没匹配,只能全部插入 ,所以需要 j+1
  • j < 0t 已经空了,但 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;
    }
}
相关推荐
吴声子夜歌1 小时前
ApacheCommons——commons-math3(科学计算与线性代数)(一)
java·线性代数·算法·apache
tachibana21 小时前
Embedding 有哪几种算法?
人工智能·算法·ai·大模型·llm·embedding·agent
吴声子夜歌2 小时前
ApacheCommons——commons-configuration2(多数据源配置统一管理与热加载)
java·开发语言·算法·apache
kyle~2 小时前
奇异值分解(SVD)
人工智能·算法·机器学习
HugoStudio_SWAN2 小时前
洛谷 B4502 [GESP202603 四级] 礼盒排序——多关键字排序与排名计数法
算法
计科杨某人2 小时前
简单算法题(基础入门题)
c++·算法·题解·入门·基础算法
Q一件事2 小时前
RWEQ计算——Wf因子
算法
zander2582 小时前
LeetCode 64. 最小路径和
算法·深度优先
find1star11 小时前
LeetCode 141:环形链表
java·算法·leetcode·链表