动态规划——子序列问题

文章目录

目录

文章目录

前言

一、动态规划思路简介

二、具体实现

1. 字符串问题

1.1 最长公共字符串

1.2.0 最长回文子串

1.2.1 最长回文子序列

2.编辑距离问题

2.1 判断子序列

2.2 编辑距离

总结


前言

上文提到动态规划的背包问题,本文继续介绍动态规划的另一种应用:子序列问题。子序列分为间断子序列与连续子序列,其中诸多问题例如最大递增子序列、最长回文串等等,都可以用动态规划的思路解决


一、动态规划思路简介

动态规划解题的关键有两步。

第一步是找出题目中量的递推关系式。动态规划的代码实现是依据递推关系展开的,找出递推关系是最关键,也是最先要处理的一件事情。

第二步是依据递推关系明确dp数组的下标含义,并进行正确的初始化。

接下来我们从一些具体问题看一下如何使用动态规划思路解决问题。

二、具体实现

1. 字符串问题

1.1 最长公共字符串

请编写一个 C 语言程序来查找两个字符串的最长公共子串(由字符串中连续的一段字符组成的字符串)

输入两个字符串str1和str2, 字符串长度范围均为[0, 200)。

输出两个字符串的最长公共子串,如果不存在公共子串,则输出“没有公共子串”;如果有多个最长公共子串,则输出str1中从左至右第一个最长公共子串。

示例输入

abbbcc
accd

示例输出

cc

解题思路:先寻找有没有递推关系式。观察发现,此类最长问题,一般有 “当前位”的最长公共字符串长度等于“前一位”的最长公共字符串长度加一。

由递推关系可知,遍历数组的方向应该从前往后遍历,并有dp[i]=dp[i-1]+1,此处i表示在字符串中的位置,dp[i]表示以第i位为结尾的公共字符串长度。

具体代码

#include<stdio.h>
#include<string.h>
int main(){char a[300]={0},b[300]={0};scanf("%s%s",a+1,b+1);
//	dp[i][j]表示a的i位,b的j位相同的情况下 从前向后数公共字串的长度int dp[300][300]={0};
//初始化为0的原因在于默认字符不相等,在遍历时检测是否相等,若相等则加一int alen=strlen(a+1),blen=strlen(b+1);int maxlen=0,maxLocation=1;for(int i=1;i<=alen;i++){for(int j=1;j<=blen;j++){if(a[i]==b[j]){dp[i][j]=dp[i-1][j-1]+1;if(maxlen<dp[i][j]){maxlen=dp[i][j];maxLocation=i-maxlen+1;//找起始位置 }}}}for(int i=0;i<maxlen;i++){printf("%c",a[maxLocation+i]);}return 0;
}

1.2.0 最长回文子串

5. 最长回文子串 - 力扣(LeetCode)

给你一个字符串 s,找到 s 中最长的回文子串。

示例 1:

输入:s = "babad"
输出:"bab"
解释:"aba" 同样是符合题意的答案。

示例 2:

输入:s = "cbbd"
输出:"bb"

解题思路:观察回文串规律,发现回文串有如下特征:若回文串字符数为奇数,则以第i位为中心向左右检索,如果左右边缘的字符相等,则回文串字符数加二。若回文串字符为偶数,则以第i位与第 i+1 位为中心向左右检索。

设dp1[i]与dp2[i],前者表示在第i位向前数有多少个回文字符,dp2同理。

代码样例

char* longestPalindrome(char* s){int dp1[1000]={0},dp2[1000]={0};int len=strlen(s);for(int i=0;i<len;i++){//dp1[i]表示对i加上中心后一半的长度while(i-dp1[i]>=0&&i+dp1[i]<len){if(s[i-dp1[i]]!=s[i+dp1[i]])break;dp1[i]++;}if(s[i]==s[i+1]&&i+1<len){while(i-dp2[i]>=0&&i+1+dp2[i]<len){if(s[i-dp2[i]]!=s[i+1+dp2[i]])break;dp2[i]++;}}}int max=2*dp1[1]-1,maxlocation=0;for(int i=0;i<len;i++){dp1[i]=2*dp1[i]-1;dp2[i]=2*dp2[i];if(max<(dp1[i]>dp2[i]?dp1[i]:dp2[i])){max=dp1[i]>dp2[i]?dp1[i]:dp2[i];maxlocation=i+1-(max+1)/2;}}char*a=(char*)malloc(1010);strncpy(a,s+maxlocation,max);a[max]=0; return a;
}

1.2.1 最长回文子序列

516. 最长回文子序列 - 力扣(LeetCode)

给你一个字符串 s ,找出其中最长的回文子序列,并返回该序列的长度。

子序列定义为:不改变剩余字符顺序的情况下,删除某些字符或者不删除任何字符形成的一个序列。

示例 1:

输入:s = "bbbab"
输出:4
解释:一个可能的最长回文子序列为 "bbbb" 。

示例 2:

输入:s = "cbbd"
输出:2
解释:一个可能的最长回文子序列为 "bb" 。

解题思路:回文数组规律一般可由中间向两边推知。故知,对于 i 到 j 的子序列而言,若s [ i ] = s [ j ] 。其最大回文子序列数必定是dp[ i +1] [ j -1 ]+2。若s [ i ]不等于s [ j ],那么考虑加入左侧元素或右侧元素后能否使子序列数增加,即比较放入左侧元素与放入右侧元素后的最大子序列数。

if(s[left]==s[right]){dp[left][right]=dp[left+1][right-1]+2;}else{int max=dp[left+1][right]>dp[left][right-1]?dp[left+1][right]:dp[left][right-1];dp[left][right]=dp[left+1][right-1];}

在动态规划解题过程中,我们只考虑按遍历顺序下的当前步未知,前面已遍历过的dp应该已知,并不再考虑它们是怎么计算得出的。因此对遍历顺序有较强的要求。

对于dp[ i ][ j ],i 表示左边界,j 表示右边界,dp[ i ][ j ]表示最大序列数。因为计算dp[ i ][ j ]需要

dp[ i+1][ j ]   dp[ i+1 ][ j-1]   dp[ i ][ j-1],所以遍历顺序应该i递减,j递增,从而保持完备性。

示例代码:

int longestPalindromeSubseq(char* s) {int dp[1010][1010]={0};//表示从i到j的最长回文序列int len=strlen(s);for(int i=0;i<len;i++)dp[i][i]=1;for(int left=len-1;left>=0;left--){for(int right=left+1;right<len;right++){if(s[left]==s[right]){dp[left][right]=dp[left+1][right-1]+2;}else{int max=dp[left+1][right]>dp[left][right-1]?dp[left+1][right]:dp[left][right-1];dp[left][right]=max;}}}return dp[0][len-1];
}

2.编辑距离问题

2.1 判断子序列

392. 判断子序列 - 力扣(LeetCode)

给定字符串 s 和 t ,判断 s 是否为 t 的子序列。

字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace""abcde"的一个子序列,而"aec"不是)。

示例1

输入:s = "abc", t = "ahbgdc"
输出:true

示例2

输入:s = "axc", t = "ahbgdc"
输出:false

解题思路f[i][j] 存储了从位置 i 开始,字符 j + 'a' 在字符串 t 中的下一个出现位置。首先初始化 f 数组并填充,之后遍历 s 中的每个字符,在 f 数组中找到对应字符的下一个出现位置并更新。如果在任何时刻无法找到字符,返回 false,否则返回 true

具体代码

bool isSubsequence(char* s, char* t) {int n = strlen(s), m = strlen(t);int f[m + 1][26];memset(f, 0, sizeof(f));for (int i = 0; i < 26; i++) {f[m][i] = m;}for (int i = m - 1; i >= 0; i--) {for (int j = 0; j < 26; j++) {if (t[i] == j + 'a')f[i][j] = i;elsef[i][j] = f[i + 1][j];}}int add = 0;for (int i = 0; i < n; i++) {if (f[add][s[i] - 'a'] == m) {return false;}add = f[add][s[i] - 'a'] + 1;}return true;
}

2.2 编辑距离

72. 编辑距离 - 力扣(LeetCode)

给你两个单词 word1 和 word2, 请返回将 word1 转换成 word2 所使用的最少操作数  。

你可以对一个单词进行如下三种操作:

  • 插入一个字符

  • 删除一个字符

  • 替换一个字符

示例1

输入:word1 = "horse", word2 = "ros"
输出:3
解释:
horse -> rorse (将 'h' 替换为 'r')
rorse -> rose (删除 'r')
rose -> ros (删除 'e')

示例2 

输入:word1 = "intention", word2 = "execution"
输出:5
解释:
intention -> inention (删除 't')
inention -> enention (将 'i' 替换为 'e')
enention -> exention (将 'n' 替换为 'x')
exention -> exection (将 'n' 替换为 'c')
exection -> execution (插入 'u')

解题思路

定义一个 dp 数组,dp[i][j] 表示将 word1[0..i-1] 转换为 word2[0..j-1] 的最小编辑距离。然后初始化 dp 数组的第一行和第一列,分别代表从空字符串到其他字符串的编辑距离。

对每一对字符 (word1[i-1], word2[j-1]),如果相等,dp[i][j] = dp[i-1][j-1],否则考虑三种操作(删除、插入、替换),选择最小值。

最终结果在 dp[n][m],其中 nm 分别是 word1word2 的长度。

具体代码

int minDistance(char* word1, char* word2) {int n = strlen(word1), m = strlen(word2);int dp[n+1][m+1];// 初始化边界条件for (int i = 0; i <= n; i++) dp[i][0] = i;for (int j = 0; j <= m; j++) dp[0][j] = j;// 填充 dp 数组for (int i = 1; i <= n; i++) {for (int j = 1; j <= m; j++) {int cost = (word1[i-1] == word2[j-1]) ? 0 : 1;dp[i][j] = fmin(fmin(dp[i-1][j] + 1, dp[i][j-1] + 1), dp[i-1][j-1] + cost);}}return dp[n][m];
}


总结

动态规划在子序列问题中应用广泛,关键是找出递推关系并设计合适的 dp 数组。通过精妙的状态转移,可以高效解决如最长公共子串、回文子序列及编辑距离等问题。

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

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

相关文章

Ubuntu24.04配置DINO-Tracker

一、引言 记录 Ubuntu 配置的第一个代码过程 二、更改conda虚拟环境的默认安装路径 鉴于不久前由于磁盘空间不足引发的重装系统的惨痛经历&#xff0c;在新系统装好后当然要先更改虚拟环境的默认安装路径。 输入指令&#xff1a; conda info可能因为我原本就没有把 Anacod…

vulnhub靶场【哈利波特】三部曲之Aragog

前言 使用virtual box虚拟机 靶机&#xff1a;Aragog : 192.168.1.101 攻击&#xff1a;kali : 192.168.1.16 主机发现 使用arp-scan -l扫描&#xff0c;在同一虚拟网卡下 信息收集 使用nmap扫描 发现22端口SSH服务&#xff0c;openssh 80端口HTTP服务&#xff0c;Apach…

顶刊算法 | 鱼鹰算法OOA-BiTCN-BiGRU-Attention多输入单输出回归预测(Maltab)

顶刊算法 | 鱼鹰算法OOA-BiTCN-BiGRU-Attention多输入单输出回归预测&#xff08;Maltab&#xff09; 目录 顶刊算法 | 鱼鹰算法OOA-BiTCN-BiGRU-Attention多输入单输出回归预测&#xff08;Maltab&#xff09;效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab实…

getchar()

getchar():从计算机终端&#xff08;一般是键盘&#xff09;输入一个字符 1、getchar返回的是字符的ASCII码值&#xff08;整数&#xff09;。 2、getchar在读取结束或者失败的时候&#xff0c;会返回EOF 输入密码并确认&#xff1a; scanf读取\n之前的内容即12345678 回车符…

动态规划-----路径问题

动态规划-----路径问题 下降最小路径和1&#xff1a;状态表示2&#xff1a;状态转移方程3 初始化4 填表顺序5 返回值6 代码实现 总结&#xff1a; 下降最小路径和 1&#xff1a;状态表示 假设&#xff1a;用dp[i][j]表示&#xff1a;到达[i,j]的最小路径 2&#xff1a;状态转…

实现PDF文档加密,访问需要密码

01. 背景 今天下午老板神秘兮兮的来问我&#xff0c;能不能做个文档加密功能&#xff0c;就是那种用户下载打开需要密码才能打开的那种效果。boss都发话了&#xff0c;那必须可以。 需求&#xff1a;将 pdf 文档经过加密处理&#xff0c;客户下载pdf文档&#xff0c;打开文档需…

HarmonyOS Next 模拟器安装与探索

HarmonyOS 5 也发布了有一段时间了&#xff0c;不知道大家实际使用的时候有没有发现一些惊喜。当然随着HarmonyOS 5的更新也带来了很多新特性&#xff0c;尤其是 HarmonyOS Next 模拟器。今天&#xff0c;我们就来探索一下这个模拟器&#xff0c;看看它能给我们的开发过程带来什…

深入探索进程间通信:System V IPC的机制与应用

目录 1、System V概述 2.共享内存&#xff08;shm&#xff09; 2.1 shmget — 创建共享内存 2.1.2 ftok&#xff08;为shmmat创建key值&#xff09; 2.1.3 为什么一块共享内存的标志信息需要用户来传递 2.2 shmat — 进程挂接共享内存 2.3 shmdt — 断开共享内存连接 2.4…

Rust : 生成日历管理markdown文件的小工具

需求&#xff1a; 拟生成以下markdown管理小工具&#xff0c;这也是我日常工作日程表。 可以输入任意时间段&#xff0c;运行后就可以生成以上的markdown文件。 一、toml [package] name "rust-workfile" version "0.1.0" edition "2021"[d…

mean,median,mode,var,std,min,max函数

剩余的函数都放在这篇里面吧 m e a n mean mean函数可以求平均值 a a a为向量时&#xff0c; m e a n ( a ) mean(a) mean(a)求向量中元素的平均值 a a a为矩阵时&#xff0c; m e a n ( a , 1 ) mean(a,1) mean(a,1)求矩阵中各列元素的平均值&#xff1b; m e a n ( a , 2 )…

Android studio 签名加固后的apk文件

Android studio打包时&#xff0c;可以选择签名类型v1和v2&#xff0c;但是在经过加固后&#xff0c;签名就不在了&#xff0c;或者只有v1签名&#xff0c;这样是不安全的。 操作流程&#xff1a; 1、Android studio 对项目进行打包&#xff0c;生成有签名的apk文件&#xff…

【计算机网络】实验2:总线型以太网的特性

实验 2&#xff1a;总线型以太网的特性 一、 实验目的 加深对MAC地址&#xff0c;IP地址&#xff0c;ARP协议的理解。 了解总线型以太网的特性&#xff08;广播&#xff0c;竞争总线&#xff0c;冲突&#xff09;。 二、 实验环境 • Cisco Packet Tracer 模拟器 三、 实…

PHP RabbitMQ连接超时问题

问题背景 Error: The connection timed out after 3 sec while awaiting incoming data 看到这个报错&#xff0c;我不以为意&#xff0c;认为是我设置的超时时间不够导致的&#xff0c;那就设置长一点 Error: The connection timed out after 300 sec while awaiting incom…

asp.net core过滤器应用

筛选器类型 授权筛选器 授权过滤器是过滤器管道的第一个被执行的过滤器&#xff0c;用于系统授权。一般不会编写自定义的授权过滤器&#xff0c;而是配置授权策略或编写自定义授权策略。简单举个例子。 using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCo…

Linux DNS解释器

作用 DNS&#xff08;Domain Name System&#xff09;是互联网上的一项服务&#xff0c;用于将域名和IP地址进行相互映射&#xff0c;使人 更方便的访问互联网 正向解析&#xff1a;域名->IP 反向解析&#xff1a;IP->域名 连接方式 DNS使用53端口监听网络 查看方法&a…

3.STM32通信接口之SPI通信---SPI实战(W25Q64存储模块介绍)《精讲》

上一节介绍了SPI的通信过程和方法&#xff0c;接下来就要进行STM32与外围模块通信了&#xff0c;这个模块是一块非易失型存储芯片&#xff0c;能够提供8MB的存储空间。接下来跟着Whappy脚步&#xff0c;进行探索新大陆吧&#xff01;【免费】W25Q64(中英文数据手册)资源-CSDN文…

嵌入式系统应用-LVGL的应用-平衡球游戏 part2

平衡球游戏 part2 4 mpu60504.1 mpu6050 介绍4.2 电路图4.3 驱动代码编写 5 游戏界面移植5.1 移植源文件5.2 添加头文件 6 参数移植6.1 4 mpu6050 4.1 mpu6050 介绍 MPU6050是一款由InvenSense公司生产的加速度计和陀螺仪传感器&#xff0c;广泛应用于消费电子、机器人等领域…

java将word docx pdf转换为图片(不需要额外下载压缩包,直接导入maven坐标)

(本代码实现的是将第1页转为图片&#xff0c;主要用于制作文件缩略图) pdf转图片容易 docx转图片麻烦&#xff0c;看其他博客可以直接导入maven坐标&#xff0c;但我知道那是需要付费且有时限的包 本着简单实用的心&#xff0c;我找到法子了 pdf转图片&#xff1a;有库直接转…

C#学写了一个程序记录日志的方法(Log类)

1.错误和警告信息单独生产文本进行记录&#xff1b; 2.日志到一定内存阈值可以打包压缩&#xff0c;单独存储起来&#xff0c;修改字段MaxLogFileSizeForCompress的值即可&#xff1b; 3.Log类调用举例&#xff1a;Log.Txt(JB.信息,“日志记录内容”,"通道1"); usi…

linux(centos) 环境部署,安装JDK,docker(mysql, redis,nginx,minio,nacos)

目录 1.安装JDK (非docker)1.1 将文件放在目录下&#xff1a; /usr/local/jdk1.2 解压至当前目录1.3 配置环境变量 2.安装docker2.1 验证centos内核2.2 安装软件工具包2.3 设置yum源2.4 查看仓库中所有docker版本&#xff0c;按需选择安装2.5 安装docker2.6 启动docker 并 开机…