当前位置: 首页 > news >正文

【LeetCode 热题 100】1143. 最长公共子序列——(解法二)递推

Problem: 1143. 最长公共子序列

文章目录

  • 整体思路
  • 完整代码
  • 时空复杂度
    • 时间复杂度:O(m * n)
    • 空间复杂度:O(m * n)

整体思路

这段代码同样旨在解决 “最长公共子序列” (LCS) 问题,但它采用的是一种 自底向上(Bottom-Up)的动态规划 方法,也称为 迭代法制表法 (Tabulation)。这种方法通过构建一个DP表(dp 二维数组),从最小的子问题开始,逐步计算出最终的解,避免了递归的开销。

该实现是解决LCS问题的标准教科书式DP模型。

  1. 状态定义与索引映射

    • 算法定义了一个二维DP数组 dp,大小为 (m+1) x (n+1)
    • dp[i][j] 的含义是:text1 的前 i 个字符(text1.substring(0, i))与 text2 的前 j 个字符(text2.substring(0, j))的最长公共子序列的长度。
    • 这是一个常见的索引偏移技巧。dp 数组的索引 ij 分别对应了 text1text2长度,而字符串的索引是 i-1j-1。这样做的好处是,dp 数组的第0行和第0列可以天然地代表空字符串的情况,简化了边界处理。
  2. 基础情况 (Base Case)

    • 在Java中,new int[m + 1][n + 1] 数组的所有元素默认初始化为0。
    • dp[0][j]dp[i][0] 始终为0。这恰好满足我们的基础情况:任何字符串与一个空字符串的最长公共子序列长度都为0。
  3. 状态转移 (State Transition)

    • 算法使用两层嵌套循环来填充DP表。ij 在循环中对应的是字符串的索引。
    • 在循环的每一步,计算 dp[i+1][j+1] 的值,它代表 text1[0...i]text2[0...j] 的LCS长度。
    • 状态转移逻辑基于对 text1.charAt(i)text2.charAt(j) 的比较:
      a. 如果 text1.charAt(i) == text2.charAt(j)
      • 这意味着这两个相同的字符可以被包含在LCS中。
      • 因此,当前LCS的长度等于 text1[0...i-1]text2[0...j-1] 的LCS长度再加1。
      • 状态转移方程为:dp[i + 1][j + 1] = dp[i][j] + 1
        b. 如果 text1.charAt(i) != text2.charAt(j)
      • 这意味着这两个不同的字符不可能同时被包含在LCS中。
      • LCS的长度取决于两种可能中较长的那一个:
        1. text1[0...i-1]text2[0...j] 的LCS,其长度为 dp[i][j+1]
        2. text1[0...i]text2[0...j-1] 的LCS,其长度为 dp[i+1][j]
      • 状态转移方程为:dp[i + 1][j + 1] = Math.max(dp[i][j + 1], dp[i + 1][j])
  4. 返回结果

    • 当所有循环结束后,dp 数组已经完全填充。我们需要的最终答案,即 text1text2 整个字符串的LCS长度,就存储在 dp[m][n] 中。

完整代码

class Solution {/*** 计算两个字符串的最长公共子序列的长度。* @param text1 第一个字符串* @param text2 第二个字符串* @return 最长公共子序列的长度*/public int longestCommonSubsequence(String text1, String text2) {int m = text1.length();int n = text2.length();// dp[i][j]: 表示 text1 的前 i 个字符与 text2 的前 j 个字符的 LCS 长度。// 使用 m+1, n+1 的大小是为了用第0行和第0列代表空字符串,简化边界处理。int[][] dp = new int[m + 1][n + 1];// i 和 j 是 text1 和 text2 的 0-based 索引for (int i = 0; i < m; i++) {for (int j = 0; j < n; j++) {// 如果当前比较的两个字符相同if (text1.charAt(i) == text2.charAt(j)) {// LCS 长度等于它们各自去掉末尾字符后的 LCS 长度 + 1。dp[i + 1][j + 1] = dp[i][j] + 1;} else {// 如果字符不同,LCS 长度等于两种情况中的较大值:// 1. text1 去掉末尾 vs text2 (dp[i][j+1])// 2. text1 vs text2 去掉末尾 (dp[i+1][j])dp[i + 1][j + 1] = Math.max(dp[i][j + 1], dp[i + 1][j]);}}}// 最终答案存储在 dp[m][n] 中,代表整个 text1 和 text2 的 LCS 长度。return dp[m][n];}
}

时空复杂度

时间复杂度:O(m * n)

  • mtext1 的长度,ntext2 的长度。
  1. 循环结构:算法使用了两层嵌套的 for 循环。
    • 外层循环从 i = 0 运行到 m-1,执行 m 次。
    • 内层循环从 j = 0 运行到 n-1,执行 n 次。
  2. 计算依据
    • 总的操作次数(主要是比较、加法和Math.max)是内外层循环次数的乘积,即 m * n

综合分析
算法的时间复杂度为 O(m * n)

空间复杂度:O(m * n)

  1. 主要存储开销:算法创建了一个名为 dp 的二维整型数组来存储动态规划的所有中间状态。
  2. 空间大小:该数组的大小是 (m + 1) * (n + 1)

综合分析
算法所需的额外空间主要由 dp 数组决定,其空间复杂度为 O(m * n)

http://www.dtcms.com/a/363729.html

相关文章:

  • 快速入门Vue3——基础语法
  • Linux文本处理工具
  • 梯度波导_FDTD_学习_代码
  • ubuntu之坑(十九)——VMware虚拟机扩容磁盘
  • git工具笔记
  • 若想将gpu的代码在昇腾npu上运行,创建docker应该创建怎么样的docker?(待完善)
  • C/C++哆啦A梦
  • Java 技术支撑 AI 系统落地:从模型部署到安全合规的企业级解决方案(二)
  • 【面试场景题】外卖平台如何扛住高峰期上千qps订单查询流量
  • Python错误调试测试——调试
  • GNU Make | C/C++项目自动构建入门
  • 【日常学习8】2025-9-3 学习控件Day2
  • 解决HyperMesh许可证与版本不匹配问题
  • 【107】基于51单片机智能炒菜机【Proteus仿真+Keil程序+报告+原理图】
  • Vue + fetchEventSource 使用 AbortController 遇到的“只能中止一次”问题解析与解决方案
  • LeetCode 844.比较含退格的字符串
  • Spring 事务原理解析:AOP 的一次完美落地
  • 高校党建信息管理系统的设计与实现-(源码+LW+可部署)
  • wpf模板之DataTemplate
  • HTML第五课:求职登记表
  • apache-jmeter-5.1.1安装部署与使用教程(小白一看就会)​
  • Docker启动两个Redis镜像并配置一主一从
  • Spring Boot数据脱敏方案
  • sed相关知识
  • C++基础组件
  • 【值得收藏】手把手教你用PyTorch构建Transformer英汉翻译系统,从训练到推理
  • 小程序蓝牙低功耗(BLE)外围设备开发指南
  • C++革命性新特性:默认实例导出(exportDefault)让单例模式变得无比简单!
  • Vue2 入门(一)介绍及Demo项目创建
  • GISBox内置免费GIS服务器:地形服务发布与应用全指南