LeetCode 62/64/5/1143多维动态规划核心题型总结

目录

一、题型总览

[二、题 1:不同路径(LeetCode 62)](#二、题 1:不同路径(LeetCode 62))

[1. 题目描述](#1. 题目描述)

[2. 核心理论](#2. 核心理论)

[3. 基础解法:二维 DP](#3. 基础解法:二维 DP)

[4. 优化解法 1:一维 DP(空间 O (n))](#4. 优化解法 1:一维 DP(空间 O (n)))

[5. 优化解法 2:数学公式(O (1) 空间)](#5. 优化解法 2:数学公式(O (1) 空间))

[三、题 2:最小路径和(LeetCode 64)](#三、题 2:最小路径和(LeetCode 64))

[1. 题目描述](#1. 题目描述)

[2. 核心理论](#2. 核心理论)

[3. 基础解法:二维 DP](#3. 基础解法:二维 DP)

[4. 优化解法:原地修改(空间 O (1))](#4. 优化解法:原地修改(空间 O (1)))

[四、题 3:最长回文子串(LeetCode 5)](#四、题 3:最长回文子串(LeetCode 5))

[1. 题目描述](#1. 题目描述)

[2. 核心理论](#2. 核心理论)

[3. 基础解法:二维 DP](#3. 基础解法:二维 DP)

[4. 优化解法:中心扩展法(空间 O (1))](#4. 优化解法:中心扩展法(空间 O (1)))

[五、题 4:最长公共子序列(LeetCode 1143)](#五、题 4:最长公共子序列(LeetCode 1143))

[1. 题目描述](#1. 题目描述)

[2. 核心理论](#2. 核心理论)

[3. 基础解法:二维 DP](#3. 基础解法:二维 DP)

[4. 优化解法:一维 DP(空间 O (n))](#4. 优化解法:一维 DP(空间 O (n)))

[六、题 5:编辑距离(LeetCode 72)](#六、题 5:编辑距离(LeetCode 72))

[1. 题目描述](#1. 题目描述)

[2. 核心理论](#2. 核心理论)

[3. 基础解法:二维 DP](#3. 基础解法:二维 DP)

[4. 优化解法:一维 DP(空间 O (n))](#4. 优化解法:一维 DP(空间 O (n)))

七、整体总结

[1. 多维 DP 的核心共性](#1. 多维 DP 的核心共性)

[2. 解题技巧](#2. 解题技巧)


本次总结覆盖 5 道多维 DP 经典中等题,涵盖「路径问题、子串 / 子序列问题」,每道题提供基础解法 + 优化解法,拆解核心逻辑与难点:

一、题型总览

题目 核心场景 基础解法(时间 / 空间) 优化解法(时间 / 空间) 核心难点
不同路径 网格路径计数 二维 DP(O (mn)/O (mn)) 一维 DP / 数学公式(O (mn)/O (n)) 边界初始化 + 路径方向约束
最小路径和 网格路径最优值 二维 DP(O (mn)/O (mn)) 原地修改(O (mn)/O (1)) 左 / 上方向的最小和递推
最长回文子串 连续子串匹配 二维 DP(O (n²)/O (n²)) 中心扩展法(O (n²)/O (1)) 回文的嵌套 + 对称特性
最长公共子序列 非连续子序列 二维 DP(O (mn)/O (mn)) 一维 DP(O (mn)/O (n)) 子序列的非连续匹配
编辑距离 字符串转换代价 二维 DP(O (mn)/O (mn)) 一维 DP(O (mn)/O (n)) 三种编辑操作的状态映射

二、题 1:不同路径(LeetCode 62)

1. 题目描述

m×n 网格,从左上角到右下角,每次只能向右 / 向下 移动,求不同路径数。示例:m=3, n=7 → 输出 28。

2. 核心理论

状态定义:dp[p][q] = 到达(p,q)的不同路径数。状态转移:dp[p][q] = dp[p-1][q] + dp[p][q-1](来自上方 / 左方的路径数之和)。

3. 基础解法:二维 DP

java 复制代码
public int uniquePaths(int m, int n) {
    int[][] dp = new int[m][n];
    // 第一行:只能从左到右,路径数恒为1
    for (int q = 0; q < n; q++) dp[0][q] = 1;
    // 第一列:只能从上到下,路径数恒为1
    for (int p = 0; p < m; p++) dp[p][0] = 1;
    // 非边界
    for (int p = 1; p < m; p++) {
        for (int q = 1; q < n; q++) {
            dp[p][q] = dp[p-1][q] + dp[p][q-1];
        }
    }
    return dp[m-1][n-1];
}

4. 优化解法 1:一维 DP(空间 O (n))

观察到dp[p][q]仅依赖上一行的 q 列当前行的 q-1 列,用一维数组滚动更新:

java 复制代码
public int uniquePaths(int m, int n) {
    int[] dp = new int[n];
    Arrays.fill(dp, 1); // 第一行初始为1
    for (int p = 1; p < m; p++) {
        for (int q = 1; q < n; q++) {
            dp[q] += dp[q-1]; // 等价于 dp[p][q] = dp[p-1][q] + dp[p][q-1]
        }
    }
    return dp[n-1];
}

5. 优化解法 2:数学公式(O (1) 空间)

本质是组合问题 :从(m-1)+(n-1)步中选m-1步向下(或n-1步向右),公式为:路径数=Cm+n−2m−1​=(m−1)!(n−1)!(m+n−2)!​

java 复制代码
public int uniquePaths(int m, int n) {
    long res = 1;
    // 计算组合数 C(m+n-2, min(m-1,n-1))
    int k = Math.min(m-1, n-1);
    for (int i = 1; i <= k; i++) {
        res = res * (m + n - 2 - i + 1) / i;
    }
    return (int) res;
}

三、题 2:最小路径和(LeetCode 64)

1. 题目描述

m×n 网格,求从左上角到右下角的最小路径和 (每次只能向右 / 向下)。示例:grid=[[1,3,1],[1,5,1],[4,2,1]] → 输出 7。

2. 核心理论

状态定义:dp[p][q] = 到达(p,q)的最小路径和。状态转移:dp[p][q] = min(dp[p-1][q], dp[p][q-1]) + grid[p][q]

3. 基础解法:二维 DP

java 复制代码
public int minPathSum(int[][] grid) {
    int m = grid.length, n = grid[0].length;
    int[][] dp = new int[m][n];
    dp[0][0] = grid[0][0];
    // 第一行
    for (int q = 1; q < n; q++) dp[0][q] = dp[0][q-1] + grid[0][q];
    // 第一列
    for (int p = 1; p < m; p++) dp[p][0] = dp[p-1][0] + grid[p][0];
    // 非边界
    for (int p = 1; p < m; p++) {
        for (int q = 1; q < n; q++) {
            dp[p][q] = Math.min(dp[p-1][q], dp[p][q-1]) + grid[p][q];
        }
    }
    return dp[m-1][n-1];
}

4. 优化解法:原地修改(空间 O (1))

直接在原数组上更新,无需额外空间:

java 复制代码
public int minPathSum(int[][] grid) {
    int m = grid.length, n = grid[0].length;
    // 第一行
    for (int q = 1; q < n; q++) grid[0][q] += grid[0][q-1];
    // 第一列
    for (int p = 1; p < m; p++) grid[p][0] += grid[p-1][0];
    // 非边界
    for (int p = 1; p < m; p++) {
        for (int q = 1; q < n; q++) {
            grid[p][q] += Math.min(grid[p-1][q], grid[p][q-1]);
        }
    }
    return grid[m-1][n-1];
}

四、题 3:最长回文子串(LeetCode 5)

1. 题目描述

求字符串中最长的连续回文子串 。示例:s="babad" → 输出"bab""aba"

2. 核心理论

状态定义:dp[left][right] = 子串s[left..right]是否为回文。状态转移:

  • s[left] == s[right],则dp[left][right] = dp[left+1][right-1](长度 > 2)或true(长度≤2)。

3. 基础解法:二维 DP

java 复制代码
public String longestPalindrome(String s) {
    int n = s.length();
    boolean[][] dp = new boolean[n][n];
    int start = 0, maxLen = 1;
    // 长度为1的子串
    for (int i = 0; i < n; i++) dp[i][i] = true;
    // 长度为2的子串
    for (int i = 0; i < n-1; i++) {
        if (s.charAt(i) == s.charAt(i+1)) {
            dp[i][i+1] = true;
            start = i;
            maxLen = 2;
        }
    }
    // 长度≥3的子串
    for (int len = 3; len <= n; len++) {
        for (int left = 0; left <= n - len; left++) {
            int right = left + len - 1;
            if (s.charAt(left) == s.charAt(right) && dp[left+1][right-1]) {
                dp[left][right] = true;
                start = left;
                maxLen = len;
            }
        }
    }
    return s.substring(start, start + maxLen);
}

4. 优化解法:中心扩展法(空间 O (1))

所有回文有一个 "中心",从中心向两边扩展:

java 复制代码
public String longestPalindrome(String s) {
    int n = s.length();
    int start = 0, maxLen = 1;
    for (int i = 0; i < n; i++) {
        // 奇数长度中心(单个字符)
        int len1 = expand(s, i, i);
        // 偶数长度中心(两个字符)
        int len2 = expand(s, i, i+1);
        int len = Math.max(len1, len2);
        if (len > maxLen) {
            start = i - (len - 1) / 2;
            maxLen = len;
        }
    }
    return s.substring(start, start + maxLen);
}

private int expand(String s, int left, int right) {
    while (left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)) {
        left--;
        right++;
    }
    return right - left - 1;
}

五、题 4:最长公共子序列(LeetCode 1143)

1. 题目描述

求两个字符串的最长公共子序列 长度(子序列非连续)。示例:text1="abcde", text2="ace" → 输出 3。

2. 核心理论

状态定义:dp[p][q] = text1前p个字符text2前q个字符的最长公共子序列长度。状态转移:

  • text1[p-1] == text2[q-1],则dp[p][q] = dp[p-1][q-1] + 1
  • 否则dp[p][q] = max(dp[p-1][q], dp[p][q-1])

3. 基础解法:二维 DP

java 复制代码
public int longestCommonSubsequence(String text1, String text2) {
    int m = text1.length(), n = text2.length();
    int[][] dp = new int[m+1][n+1];
    for (int p = 1; p <= m; p++) {
        for (int q = 1; q <= n; q++) {
            if (text1.charAt(p-1) == text2.charAt(q-1)) {
                dp[p][q] = dp[p-1][q-1] + 1;
            } else {
                dp[p][q] = Math.max(dp[p-1][q], dp[p][q-1]);
            }
        }
    }
    return dp[m][n];
}

4. 优化解法:一维 DP(空间 O (n))

观察到dp[p][q]仅依赖上一行的 q 列当前行的 q-1 列,用一维数组:

java 复制代码
public int longestCommonSubsequence(String text1, String text2) {
    int m = text1.length(), n = text2.length();
    int[] dp = new int[n+1];
    for (int p = 1; p <= m; p++) {
        int prev = dp[0]; // 保存dp[p-1][q-1]
        for (int q = 1; q <= n; q++) {
            int temp = dp[q]; // 临时保存当前dp[q](即dp[p-1][q])
            if (text1.charAt(p-1) == text2.charAt(q-1)) {
                dp[q] = prev + 1;
            } else {
                dp[q] = Math.max(dp[q], dp[q-1]);
            }
            prev = temp;
        }
    }
    return dp[n];
}

六、题 5:编辑距离(LeetCode 72)

1. 题目描述

求将word1转换为word2最少操作数 (操作:插入、删除、替换)。示例:word1="horse", word2="ros" → 输出 3。

2. 核心理论

状态定义:dp[p][q] = 将word1前p个字符转为word2前q个字符的最少操作数。状态转移:

  • word1[p-1] == word2[q-1],则dp[p][q] = dp[p-1][q-1]
  • 否则dp[p][q] = min(dp[p-1][q], dp[p][q-1], dp[p-1][q-1]) + 1(删除、插入、替换)。

3. 基础解法:二维 DP

java 复制代码
public int minDistance(String word1, String word2) {
    int m = word1.length(), n = word2.length();
    int[][] dp = new int[m+1][n+1];
    // 初始化:空串转成word2前q个字符(插入q次)
    for (int q = 0; q <= n; q++) dp[0][q] = q;
    // 初始化:word1前p个字符转成空串(删除p次)
    for (int p = 0; p <= m; p++) dp[p][0] = p;
    // 递推
    for (int p = 1; p <= m; p++) {
        for (int q = 1; q <= n; q++) {
            if (word1.charAt(p-1) == word2.charAt(q-1)) {
                dp[p][q] = dp[p-1][q-1];
            } else {
                dp[p][q] = Math.min(Math.min(dp[p-1][q], dp[p][q-1]), dp[p-1][q-1]) + 1;
            }
        }
    }
    return dp[m][n];
}

4. 优化解法:一维 DP(空间 O (n))

用一维数组滚动更新,保存上一行状态:

java 复制代码
public int minDistance(String word1, String word2) {
    int m = word1.length(), n = word2.length();
    int[] dp = new int[n+1];
    // 初始化
    for (int q = 0; q <= n; q++) dp[q] = q;
    for (int p = 1; p <= m; p++) {
        int prev = dp[0]; // 保存dp[p-1][0]
        dp[0] = p; // 当前行的dp[0](删除p次)
        for (int q = 1; q <= n; q++) {
            int temp = dp[q]; // 保存dp[p-1][q]
            if (word1.charAt(p-1) == word2.charAt(q-1)) {
                dp[q] = prev;
            } else {
                dp[q] = Math.min(Math.min(dp[q], dp[q-1]), prev) + 1;
            }
            prev = temp;
        }
    }
    return dp[n];
}

七、整体总结

1. 多维 DP 的核心共性

  • 状态定义:多以 "前 p 个 / 前 q 个""到达 (p,q) 位置" 为核心,拆分问题规模;
  • 状态转移:依赖 "更小的子问题"(如左 / 上位置、前 p-1/q-1 个字符);
  • 优化方向:通过 "滚动数组" 或 "原地修改" 将空间复杂度从 O (mn) 降到 O (n) 或 O (1)。

2. 解题技巧

  • 路径问题:关注移动方向,边界初始化(第一行 / 列);
  • 子串 / 子序列问题:区分 "连续(子串)" 与 "非连续(子序列)",子串需绑定 "左右边界",子序列需绑定 "前 p/q 个";
  • 字符串问题:编辑距离、最长公共子序列等,状态定义多为 "前 p 个字符转前 q 个字符"。

相关推荐
鹿角片ljp2 小时前
力扣 83: 删除排序链表中的重复元素(Java实现)
java·leetcode·链表
LYFlied2 小时前
【每日算法】LeetCode 208. 实现 Trie (前缀树)
数据结构·算法·leetcode·面试·职场和发展
代码游侠2 小时前
应用——MPlayer 媒体播放器系统代码详解
linux·运维·笔记·学习·算法
学编程就要猛2 小时前
算法:3.快乐数
java·算法
AI科技星2 小时前
统一场论框架下万有引力常数的量子几何涌现与光速关联
数据结构·人工智能·算法·机器学习·重构
仰泳的熊猫2 小时前
1109 Group Photo
数据结构·c++·算法·pat考试
未来之窗软件服务2 小时前
幽冥大陆(五十八)php1024位密码生成—东方仙盟筑基期
开发语言·算法·仙盟创梦ide·东方仙盟
不解风水3 小时前
【教程笔记】KalmanFilter
笔记·学习·算法·矩阵·ekf
2401_841495643 小时前
【数据结构】最短路径的求解
数据结构·动态规划·贪心·ipython·最短路径·迪杰斯特拉算法·弗洛伊德算法