【算法 - 动态规划】最长公共子序列问题

news2025/1/13 17:45:06

在上两篇文章中,我们将 暴力递归 逐步修改成为 动态规划 ,并介绍了有严格 dp表依赖 和无表依赖结构的解题方法。其中,前篇文章中的纸牌博弈问题属于 [L , R]上范围尝试模型。该模型给定一个范围,在该范围上进行尝试,套路就是 思考 [L ,R] 两端该如何取舍。

本篇文章我们学习一个新的模型: 样本对应模型,该模型的套路就是 以结尾位置为出发点,思考两个样本的结尾都会产生哪些可能性

力扣1143:最长公共子序列

给定两个字符串 text1 和 text2,返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 ,返回 0 。

一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。

两个字符串的 公共子序列 是这两个字符串所共同拥有的子序列。

示例 1:

输入: text1 = “abcde”, text2 = “ace”

*输出:*3

*解释:*最长公共子序列是 “ace” ,它的长度为 3 。

示例 2:

*输入:*text1 = “abc”, text2 = “def”

*输出:*0

*解释:*两个字符串没有公共子序列,返回 0 。

首先我们依然采用最朴素的 暴力递归 来思考这道题目。

递归的准备

定义递归函数的功能: 返回 str1 中 [0...i] ,str2 中 [0...j] 范围上两个字符串的最长公共子序列。

思考递归需要的参数: str1, str2 两个字符串,分别要比较的范围 i, j。

明确递归的边界条件: 当两个字符串长度均为 1 时:若两个字符相同返回 1 ,不同返回 0 。

思路

这道题就是典型的 样本对应模型 ,因此,递归就可以按照 两个样本的结尾都会产生哪些可能性 的思路来划分情况:

  • 公共子序列 既不以 str1 结尾,也不以 str2 结尾;
  • 公共子序列 str1 结尾,不以 str2 结尾;
  • 公共子序列 不以 str1 结尾, str2 结尾;
  • 公共子序列 既以 str1 结尾,也以 str2 结尾。

因为要求 最长 子序列,因此要返回这四种情况当中的最大值。

代码

public static int longestCommonSubsequence(String s1, String s2) {
    if (s1 == null || s2 == null || s1.length() == 0 || s2.length() == 0) {
        return 0;
    }
    char[] str1 = s1.toCharArray();
    char[] str2 = s2.toCharArray();
    return process(str1, str2, str1.length - 1, str2.length - 1);
}

public static int process(char[] str1, char[] str2, int i, int j) {
    if (i == 0 && j == 0) {
        return str1[i] == str2[j] ? 1 : 0;
    } else if (i == 0) {
        return str1[i] == str2[j] ? 1 : process(str1, str2, i, j - 1);
    } else if (j == 0) {
        return str1[i] == str2[j] ? 1 : process(str1, str2, i - 1, j);
    } else {
        int p1 = process(str1, str2, i - 1, j - 1);
        int p2 = process(str1, str2, i, j - 1);
        int p3 = process(str1, str2, i - 1, j);
        int p4 = str1[i] == str2[j] ? 1 + process(str1, str2, i - 1, j - 1) : 0;
        return Math.max(Math.max(p1, p2), Math.max(p3, p4));
    }
}

代码解释

因为递归调用中使用到了 i-1 , j-1,因此需要做边界处理。例如:当str1 中只剩下一个字符时,若与 str2 最后一个字符相等即找到了此时最长子序列,否则排除掉 str2 的最后一个字符继续递归寻找。


写出该暴力版的递归之后发现,最终要求的是最大值,因此其实 p1 的情况是一定不会比 p2 ,p3 的情况返回值要大,所以可以减少一次递归的调用,做个小小的优化。

动态规划版

public static int longestCommonSubsequence(String s1, String s2) {
    if (s1 == null || s2 == null || s1.length() == 0 || s2.length() == 0) {
        return 0;
    }
    char[] str1 = s1.toCharArray();
    char[] str2 = s2.toCharArray();
    int N = str1.length;
    int M = str2.length;
    int[][] dp = new int[N][M];
    // 处理 str1 和 str2 均剩下一个字符的情况
    dp[0][0] = 1;
    // 处理 str1 剩下一个字符的情况 
    for (int j = 1; j < M; j++) {
        dp[0][j] = str1[0] == str2[j] ? 1 : dp[0][j - 1];
    }
    // 处理 str2 剩下一个字符的情况 
    for (int i = 1; i < N; i++) {
        dp[i][0] = str1[i] == str2[0] ? 1 : dp[i - 1][0];
    }
    // str1 str2 均有多个字符的情况
    for (int i = 1; i < N; i++) {
        for (int j = 1; j < M; j++) {
            int p1 = dp[i][j - 1];
            int p2 = dp[i - 1][j];
            int p3 = str1[i] == str2[j] ? 1 + dp[i - 1][j - 1] : 0;
            dp[i][j] = Math.max(Math.max(p1, p2), p3);
        }
    }
    return dp[N - 1][M - 1];
}

代码解释

根据递归函数修改出这套动态规划版代码就非常容易了。

以 str1 = “abcde”, str2 = “ace” 为例

可变的参数有两个:str1str2 判断长度范围的 ij。因此,需要设置一个二维的 dp 表数组,由于 i, j 的取值范围从 0 开始到字符串长度减一,因此数组大小设置为 dp[N][M]

递归代码 i==0 && j==0 可知,初始只有 dp[0][0] 的值为 1 。

i == 0j == 0时,只依赖左侧或上侧的数值。

当 str1 和 str2 均有多个字符时,会依赖 左,上,左上 三个地方的 最大值


由递归调用函数 process(str1, str2, str1.length - 1, str2.length - 1) 可知,最终要求的是 dp[N - 1][M - 1] 的值 3

总结

本篇文章我们学习了一个新的分析模型 —— 样本对应模型 。该模型的套路就是: 以结尾位置为出发点,思考两个样本的结尾都会产生哪些可能性

下篇文章我们继续学习类似的题型,但分析模型可以完全不一样哦,敬请期待!

~ 点赞 ~ 关注 ~ 不迷路 ~!!!

------------- 往期回顾 -------------
【算法 - 动态规划】力扣 691. 贴纸拼词
【算法 - 动态规划】原来写出动态规划如此简单!
【算法 - 动态规划】从零开始学动态规划!(总纲)
【堆 - 专题】“加强堆” 解决 TopK 问题!
AC 此题,链表无敌!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1459240.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【AI数字人-论文】AD-NeRF论文

文章目录 NeRFAD-NeRF模型NeRF体渲染个体NeRF表示背景和姿态编辑 loss 参考 NeRF 将一个连续的场景表示为一个输入为5D向量的函数&#xff0c;这个函数为NeRF函数&#xff0c;它的输入由一个空间点的3D位置 x ( x , y , z ) \mathbf{x} \left( x, y, z \right) x(x,y,z)和它…

C# Winfrom实现的肺炎全国疫情实时信息图

运行结果&#xff1a; using System; using System.Drawing; using System.Text; using NSoup; using NSoup.Nodes; using System.IO; using System.Net; using System.Text.RegularExpressions; using System.Windows.Forms;namespace Pneumonia {public partial class MainFo…

IO 作业 24/2/20

一、思维导图 二、习题 #include <myhead.h> int main(int argc, const char *argv[]) {FILE *fpNULL;FILE *fqNULL;pid_t pidfork();if(pid>0){if((fpfopen("./text.txt","r"))NULL){perror("fopen error");return -1;} if((f…

unity学习(33)——角色选取界面(原版)

10ARPG网络游戏编程实践&#xff08;十&#xff09;&#xff1a;角色选择UI及创建面板制作&#xff08;一&#xff09;&#xff08;流畅&#xff09;_哔哩哔哩_bilibili 角色选择界面教程中是这样的&#xff01;&#xff08;这个美工肯定是不能拿出去卖的&#xff0c;但是是有…

命令行窗口文本复制到 Word 格式保持不变

命令行窗口文本复制到 Word 格式保持不变 References 标题栏右键 -> 编辑 -> 标记 / 全选 标题栏右键 -> 编辑 -> 复制 粘贴到 Notepad 中&#xff0c;语言栏设置对应语言&#xff0c;格式可以保持不变 复制文本粘贴到 Excel 中 选中 Excel 中文本复制&#xf…

LeetCode21.合并两个有序链表

题目 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例 &#xff1a; 输入&#xff1a;l1 [1,2,4], l2 [1,3,4] 输出&#xff1a;[1,1,2,3,4,4] 思路 创建一个新的链表头节点&#xff08;dummyNode&#xff09;和一个…

NVIDIA Corporation 在 GitHub 的官方主页

NVIDIA Corporation 在 GitHub 的官方主页 References https://github.com/NVIDIA References [1] Yongqiang Cheng, https://yongqiang.blog.csdn.net/

Prometheus 教程

目录 一、简介二、下载安装1、安装 prometheus2、安装 alertmanager3、安装 grafana4、安装 node_exporter5、安装 mysqld_exporter 一、简介 Prometheus 是一个开源的系统监控和警报工具。它最初由 SoundCloud 开发&#xff0c;并于 2012 年发布为开源项目。Prometheus 专注于…

服务器遭受 DDoS 攻击的常见迹象有哪些?

服务器遭受 DDoS 攻击的现象很常见&#xff0c;并且有时不容易预防&#xff0c;有部分原因是它们的形式多种多样&#xff0c;而且黑客手段越来越隐蔽。如果您怀疑自己可能遭受 DDoS 攻击&#xff0c;可以寻找多种迹象。以下是 DDoS 攻击的5个常见迹象&#xff1a; 1.网络流量无…

星宸科技SSC369G 双4K高性价比AI IPC方案

一、方案描述 SSC369G 双4K高性价比AI IPC方案采用主芯片SSC369G&#xff0c;内核为CA55四核最高主频为1.5Ghz处理器。SOC内置集成一个64位的四核RISC处理器&#xff0c;先进的图像信号处理器&#xff08;ISP&#xff09;&#xff0c;高性能的H.265/H.264/MJPEG视频编解码器&a…

基于stm32F103的蜂鸣器周期发声实验

蜂鸣器作为一种声音报警器件,应用广泛。本实验基于stm32F103单片机,通过控制蜂鸣器的IO口电平电压,使其周期性地进行电平翻转,从而驱动蜂鸣器发出周期性的鸣叫声。该实验主要运用了stm32的GPIO和定时器TIM的相关功能,不仅可以巩固这些外设的使用,也可以通过改变时间参数,控制蜂…

利用iSCSI服务部署IP SAN网络存储服务

一、配置环境&#xff08;Vmware WorkStation虚拟环境&#xff09; 服务端与客户端OS&#xff1a;openEuler 22.03-LTS CPU&#xff1a;1U1C 内存&#xff1a;2G 硬盘&#xff1a;5个SCSI磁盘&#xff0c;其中一个作为系统盘&#xff0c;另外四个配置为RAID5阵列 服务器IP…

【论文精读】ESViT

摘要 基于transformer的SSL方法在ImageNet线性检测任务上取得了最先进的性能&#xff0c;其关键原因在于使用了基于对比学习方法训练单尺度Transformer架构。尽管其简单有效&#xff0c;但现有的基于transformer的SSL&#xff08;自监督学习&#xff09;方法需要大量的计算资源…

网络协议汇总

1.HTTP协议 1.认识URL 平时我们俗称的 "网址" 其实就是说的 URL URL中的字符只能是ASCII字符&#xff0c;但是ASCII字符比较少&#xff0c;而URL则常常包含ASCII字符集以外的字符&#xff0c;如非英语字符、汉字、特殊符号等等&#xff0c;所以要对URL进行转换。这个…

举例说明什么是人机耦合

在呼叫中心行业&#xff0c;人机耦合是指将计算机自动化技术与人工服务相结合&#xff0c;以提高呼叫中心的效率和服务质量。具体来说&#xff0c;它包括通过智能语音识别、自然语言处理、机器学习等技术实现自动应答、自动导航、自动响应等功能&#xff0c;以及将人工客服与智…

企业级 文件传输加密应用,干货分享

企业级 文件传输加密应用 简历一直在投&#xff0c;一直无音讯&#xff0c;今天我又从硬盘里翻出一个 好玩的加密软件&#xff0c;这个是 2017年的时候和荷兰某世界500强公司合作的小项目。 今天分享给大家 。 文章目录 企业级 文件传输加密应用1.目的2.软件介绍3.下载好 安装…

C++:C++入门基础

创作不易&#xff0c;感谢三连 &#xff01;&#xff01; 一、什么是C C语言是结构化和模块化的语言&#xff0c;适合处理较小规模的程序。对于复杂的问题&#xff0c;规模较大的程序&#xff0c;需要高度的抽象和建模时&#xff0c;C语言则不合适。为了解决软件危机&#xff…

MySql重要知识梳理

文章目录 一.索引1.索引概述2.索引优缺点3. 索引结构为什么InnoDB存储引擎选择使用Btree索引结构? 4.索引分类思考InnoDB主键索引的Btree高度为多高? 5. 索引语法1.索引语法2.sql性能分析1.SQL执行频率2.慢查询日志3.explain执行计划 3.索引使用规则1.最左前缀法则2.索引失效…

vue3组件通信方式汇总

前言&#xff1a;本文默认读者有JS基础和Vue基础&#xff0c;如果没有这个两个基础&#xff0c;可能阅读比较困难&#xff0c;建议先看下官方文档&#xff0c;当然&#xff0c;也欢迎评论交流&#x1f601; 通信方式总结 常见搭配形式 一、props&#xff08;使用频率最高&#…

Windows 自带的 Linux 子系统(WSL)安装与使用

WSL官网安装教程&#xff1a; https://learn.microsoft.com/zh-cn/windows/wsl/install Windows 自带的Linux子系统&#xff0c;比用VM什么的香太多了。可以自己看官方教程&#xff0c;也可以以下步骤完成。 如果中间遇到我没遇到的问题百度&#xff0c;可以在评论区评论&#…