[算法笔记]最长递增子序列和编辑距离

news2025/3/1 0:22:36

在这里插入图片描述

最长递增子序列

例如对于

a[] = {2,1,5,3,6,4,8,9,7}

其最长递增子序列为{1,3,4,8,9}所以长度(或者说是结果)为5。


对于a[0...n-1],用dp[i]表示a[0...i]中以a[i]结尾的最长递增子序列长度

其状态状态方程:

dp[i]=1						// 0≤i≤n-1
dp[i]=max(dp[i],dp[j]+1])	// 若a[i]>a[j], 0≤i≤n-1, 0≤j≤i-1
// 第二种情况其实就是第i个和之前所有的比一遍,看看

其实就是对于a[i],跟前面的比,看看能不能组成更长的(a[i]是否大于a[j]),能(大于)就在状态表里拿到原来的数据(dp[j])并+1(dp[i]=dp[j]+1),不能(小于)就把它这个状态写为1(dp[i]=1

它也是提前把dp填充一遍,填充为1

/**
 * @param arr int整型一维数组 给定的数组
 * @param arrLen int arr数组长度
 * @return int整型
 */
int LIS(int* arr, int arrLen ) {
    // write code here
    if(arrLen==0)return 0;
    int dp[1001];
    int max = 1;
    for(int i = 0; i < arrLen; ++i){
        dp[i]=1;
        for(int j = 0;j < i;++j){
            if(arr[i]>arr[j]){
                dp[i]=dp[i]>dp[j]+1?dp[i]:dp[j]+1;
            }
        }
        if(max < dp[i])max = dp[i];
    }
    return max;
}

两重循环,时间复杂度 O ( n ) O(n) O(n)

相关问题:[牛客]BM71 最长上升子序列(一)


大佬代码:

int LIS(int* arr, int arrLen ) {
    // write code here
    if(arrLen<=1) return arrLen;
    int list[1000]={0}, end=0;
    list[0]=arr[0];
    for(int i=1; i<arrLen; i++)
    {
        if(arr[i]>list[end])
        {
            list[++end]=arr[i];
        }
        else if(arr[i]<list[end])
        {
            int high=end, low=0, mid;
            while(high>=low)
            {
                mid=low+(high-low)/2;
                if(list[mid]==arr[i]) break;
                else if(list[mid]>arr[i])
                {
                   high=mid-1;
                }
                else
                {
                   low=mid+1;
                }
            }
            if(list[mid]>arr[i]) list[mid]=arr[i];
            else if(list[mid]<arr[i]) list[mid+1]=arr[i];
        }
    }
    return end+1;
}

编辑距离

编辑距离问题就是:

设A和B是两个字符串。现在要用最少的字符操作次数,将字符串A转换为字符串B。

其中字符操作共有3种:

  1. 插入一个字符
  2. 删除一个字符
  3. 将一个字符替换另一个字符

例如A="sfdqxbw"B="gfdgw",结果为4


设计一个动态规划二维数组dp,其中dp[i][j]表示将a[0..i-1](1≤i≤m)b[0..j-1](1≤j≤n)的最优编辑距离(即a[0..i-1]转换为b[0..j-1]的最少操作次数)

分两种情况:一是B全空,则结果为A.length()(当然,A空也类似),另一种则是两队均非空,此时每个串都取1个字符作为比较串,比较这两串各自最后的字符,依据操作结果,扩大串的规模,进行后续操作

看当前比较串的末尾字符
两串有没有空串
有一串空
结果就是另一串的长度
都不空
三种操作之一
替换
插入
删除

dp[i][j]a[0...i]b[0...j]最小编辑距离

由此,增删改的操作对于dp[i][j]的下标变化也是不一样的:

// a[i-1]替换为b[j-1]
dp[i][j]=dp[i-1][j-1]+1
// a[i-1]后面插入b[j-1]
dp[i][j]=dp[i][j-1]+1
// 删除a[i-1]
dp[i][j]=dp[i-1][j]+1

故有状态转移方程:

// 当a[i-1]=b[j-1]
dp[i][j]==dp[i-1][j-1]
// 当a[i-1]≠b[j-1]
dp[i][j]=max(dp[i-1][j-1]+1,dp[i][j-1]+1,dp[i-1][j]+1)

一开始思考,这个“后面插入”也不是很直观。然而比如说对于A="sfdqxbw"B="gfdgw",那么dp[2][3]就是"sf"""gfd,这里最优操作是有“后面插入”这一步的。即先“后面插入”d,然后再修改s为g,总共为2。正是因为“后面插入”,才不能保证前面的内容和B中的相同,所以才有dp[i][j]=dp[i][j-1]+1

所有状态转移方程中,所有的下标减一都是因为原先的位置已经解决了,可以缩小规模。

不严格地说,这个dp数组是有边界条件的,如下:

for(int i = 1; i < a.length(); ++i){
	dp[i][0]=i;
}
for(int j = 1; j < b.length(); ++j){
	dp[0][j]=j;
}

相关题目:[牛客]BM75 编辑距离(一)

class Solution {
public:
    int editDistance(string str1, string str2) {
        // write code here
        if(str1.length() == 0){
            return str2.length();
        }
        else if(str2.length() == 0){
            return str1.length();
        }
        int dp[1001][1001];
        for(int i = 1; i <= str1.length(); ++i){
            dp[i][0]=i;
        }
        for(int j = 1; j <= str2.length(); ++j){
            dp[0][j]=j;
        }
        for(int i = 1; i <=str1.length();++i){
            for(int j = 1; j <= str2.length(); ++j){
                if(str1[i-1] == str2[j-1]){
                    dp[i][j] = dp[i-1][j-1];
                }
                else{
                    dp[i][j]=min(min(dp[i-1][j-1], dp[i][j-1]), dp[i-1][j])+1;
                }
            }
        }
        return dp[str1.length()][str2.length()];
    }
};

然后例行仰望大佬代码

class Solution {
public:
    int editDistance(string str1, string str2) {
        int m=str1.size(),n=str2.size();
        vector<vector<int>> dp(2,vector<int>(n+1,0));
        for(int j=0;j<=n;j++) dp[0][j]=j;
        for(int i=1;i<=m;i++){
            dp[i%2][0]=i;
            for(int j=1;j<=n;j++){
                if(str1[i-1]==str2[j-1])
                    dp[i%2][j]=dp[(i-1)%2][j-1];
                else dp[i%2][j]=min({dp[(i-1)%2][j],dp[i%2][j-1],dp[(i-1)%2][j-1]})+1;
            }
        }
        return dp[m%2][n];
        // write code here
    }
};

没用数组用的vector,节省空间。

然后是这个对2取余的操作。将dp8*3缩减到了2*4(2行4列),事实上无论输入是什么,dp都是两行n列,n取决于str2长度

还是以题目输入为例:"nowcoder"和"new"

// 这是正常解法的dp结果
// printf("%d ",dp[i][j]);
0 1 2 3
1 0 1 2
2 1 1 2
3 2 2 1
4 3 3 2
5 4 4 3
6 5 5 4
7 6 5 5
8 7 6 6
//printf("%d dp[%d][%d] = %d\n",i,i%2,j,dp[i%2][j]);
1 dp[1][1] = 0
1 dp[1][2] = 1
1 dp[1][3] = 2
// ---人为添加的横线1---
2 dp[0][1] = 1
2 dp[0][2] = 1
2 dp[0][3] = 2
3 dp[1][1] = 2
3 dp[1][2] = 2
3 dp[1][3] = 1
4 dp[0][1] = 3
4 dp[0][2] = 3
4 dp[0][3] = 2
5 dp[1][1] = 4
5 dp[1][2] = 4
5 dp[1][3] = 3
6 dp[0][1] = 5
6 dp[0][2] = 5
6 dp[0][3] = 4
7 dp[1][1] = 6
7 dp[1][2] = 5
7 dp[1][3] = 5
8 dp[0][1] = 7
8 dp[0][2] = 6
8 dp[0][3] = 6

截止我“人为添加的横线1”那里,此时两种方法的dp数组内容是一样的,即

0 1 2 3
1 0 1 2

然后,原先方法本应该写dp[2][j],并dp[2][j]用到了dp[1][j]的数据。此时还好,但是对于dp[i][j],会用到dp[i][j]的数据。因为对于dp[i][j],总是和上方。左方,和左上方三个格子比较,此时涉及两行内容,即第i行和第i-1行。

但是在新方法里面没有,那么多,只有两行,不过不要紧,把i=2行数据写到第0行,此时第一行就是i-1=1行的数据,会被用到。

同理,写入i%2行时,(i-1)%2总是它的上一行

某种程度上,可以理解为滑动窗口

在代码中的体现就是:

if(str1[i-1]==str2[j-1])
	dp[i%2][j]=dp[(i-1)%2][j-1];
else
	dp[i%2][j]=min({dp[(i-1)%2][j],dp[i%2][j-1],dp[(i-1)%2][j-1]})+1;

再回去看两种方法的最后两行,发现内容是一样的(除了两行颠倒了一下),也得到了印证。

不难发现,取余dp的应用条件在于,最后的结果不会出现在dp的任意位置。

在本例中,结果只能是dp的右下角。所以不会再用到前面的信息,可以被覆写掉。

另外还需要注意的是下列代码的位置

dp[i%2][0]=i;

因为行是滚动的,所以行首也是再变的,因此在每次i变化时,动态载入的行首(放在for循环内)。

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

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

相关文章

【无人机通信优化】基于粒子群算法的多跳无线网络部署优化附matlab代码

✅作者简介&#xff1a;热爱科研的Matlab仿真开发者&#xff0c;修心和技术同步精进&#xff0c;matlab项目合作可私信。 &#x1f34e;个人主页&#xff1a;Matlab科研工作室 &#x1f34a;个人信条&#xff1a;格物致知。 更多Matlab仿真内容点击&#x1f447; 智能优化算法 …

[附源码]SSM计算机毕业设计时事资讯平台JAVA

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

Date对象

文章目录Date日期对象Date对象的创建格式化日期3.获取Date总的毫秒数(时间戳)&#xff0c;是距离1970年1月1日过了多少毫秒数。二&#xff1a;常用时间获取方法三&#xff1a;日期设置方法四&#xff1a;时间转字符串菜鸟工具&#xff1a;https://www.runoob.com/jsref/jsref-o…

在Express框架使用ORM模型访问关系型数据库

一、ORM模型&#xff1a;设计思想&#xff0c;主要目的是简化计算机程序访问数据库 1、ORM&#xff1a;对象关系模型(对象关系映射) Object Releastion Model,将程序中的对象和数据库中关系(表格)进行映射。可以使开发者在程序中方便的对数据库进行操作(用户在程序操作对对象实…

【网页制作课作业】用HTML+CSS制作一个简单的学校网页(9页)

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

一文熟悉 Go 的分支结构(if - else-if - else、switch)

哈喽大家好&#xff0c;我是陈明勇&#xff0c;今天分享的知识是 Go 的分支结构。如果本文对你有帮助&#xff0c;不妨点个赞&#xff0c;如果你是 Go 语言初学者&#xff0c;不妨点个关注&#xff0c;一起成长一起进步&#xff0c;如果本文有错误的地方&#xff0c;欢迎指出&a…

Python爬虫脚本+XML解析实现自动保存某商城的商品图

文章目录 1.背景介绍2.代码分析2.1.创建图片保存的目录2.2.定一下载函数2.3.发送请求解析数据2.源代码(全)1.背景介绍 Python脚本可以实现数据的爬取,而XML可以解析网页数据。将Python爬虫脚本与XML解析功能相结合,可以实现自动保存某商城的商品图功能。 注:本功能仅用于…

基于AlexNet卷积神经网络的手写体数字识别系统研究-附Matlab代码

⭕⭕ 目 录 ⭕⭕✳️ 一、引言✳️ 二、手写体数字识别系统✳️ 2.1 MNIST 数据集✳️ 2.2 CNN✳️ 2.3 网络训练✳️ 三、手写体数字识别结果✳️ 四、参考文献✳️ 五、Matlab代码获取✳️ 一、引言 手写数字识别是计算机视觉领域中的一个重要应用&#xff0c;已广泛应用在很…

电动汽车充电站的最优选址matlab程序

​摘要&#xff1a;以规划期内充电站的总成本 &#xff08;包括投资、运行和维护成本&#xff09;和网损费用之和最小为目标&#xff0c;考虑了相关的约束条件&#xff0c;构造了电动汽车充电站最优规划的数学模型&#xff0c; 关键词&#xff1a;电动汽车&#xff1b;充电站&a…

角色扮演?一款跨平台可移植开源游戏

程序员宝藏库&#xff1a;gitee.com/sharetech_lee/CS-Books-Store DevWeekly收集整理每周优质开发者内容&#xff0c;包括开源项目、资源工具、技术文章等方面。 每周五定期发布&#xff0c;同步更新到 知乎&#xff1a;Jackpop 。 欢迎大家投稿&#xff0c;提交issue&#…

支付系统设计概览

前言 就个人对支付的一些理解和经验&#xff0c;在此编辑出来和大家一起交流分享。请大佬多多指正。 在各种互联网场景中&#xff0c;牵扯到交易的情况&#xff0c;大多都需要支付系统的支持。支付系统往往不是一蹴而就的&#xff0c;往往都是随着业务的不断扩展&#xff0c;…

【python】面向对象程序设计(基础篇)

个人主页&#xff1a;天寒雨落的博客_CSDN博客-初学者入门C语言,python,数据库领域博主 &#x1f4ac; 热门专栏&#xff1a;python_天寒雨落的博客-CSDN博客 ​每日赠语&#xff1a;没有窘迫的失败&#xff0c;就不会有自豪的成功&#xff1b;失败不可怕&#xff0c;只要能从失…

浅识vue的虚拟DOM和渲染器

虚拟DOM本质上是对DOM的抽象描述&#xff0c;就是一个普通的js对象。他身上的属性要比真实DOM的属性要少得多。 在一定情况下&#xff0c;使用虚拟DOM的性能要逊于直接使用真实DOM。 例如&#xff0c;在页面一开始的时候&#xff0c;Vue需要先通过生成虚拟DOM树&#xff0c;在…

【雷达通信】雷达探测项目仿真附Matlab代码

✅作者简介&#xff1a;热爱科研的Matlab仿真开发者&#xff0c;修心和技术同步精进&#xff0c;matlab项目合作可私信。 &#x1f34e;个人主页&#xff1a;Matlab科研工作室 &#x1f34a;个人信条&#xff1a;格物致知。 更多Matlab仿真内容点击&#x1f447; 智能优化算法 …

《恋上数据结构与算法》第1季:链表原理实现(图文并茂)

数据结构与算法的学习笔记目录&#xff1a;《恋上数据结构与算法》的学习笔记 目录索引链表原理实现一、链表二、链表的设计三、链表的接口设计四、链表接口的实现1. 索引越界的判断2. 根据索引查找指定节点3. 添加数据4. 插入元素5. 删除元素6. 清空元素7. 修改元素8. 查找元素…

傻白入门芯片设计,RDL/Interposer/EMIB/TSV(三)

目录 一、再分配层&#xff08;RDL&#xff09; 二、硅中介层&#xff08;Si Interposer&#xff09;&#xff1a;Active and Passive 三、嵌入式硅桥&#xff08;EMIB&#xff09; 四、硅通孔 TSV&#xff08;Through Silicon Vias&#xff09; 一、再分配层&#xff08;R…

CCF CSP认证2022年6月 归一化处理、寻宝!大冒险!、光线追踪

这是我第一次参加了这次CSP考试&#xff0c;300分&#xff0c;写了124三题&#xff0c;模拟题到现在都没看过题面没看&#xff0c;笑&#xff0c;t4写成模拟加数据结构&#xff0c;200行&#xff0c;因为一个小错误调了1h&#xff0c;错失了大好机会。考试环境的VSC配置的字体太…

[一篇读懂]C语言十讲:单链表的新建、查找

[一篇读懂]C语言十讲&#xff1a;单链表的新建、查找1. 与408关联解析及本节内容介绍1 与408关联解析2 本节内容介绍2. 头插法新建链表实战3. 尾插法新建链表实战4. 按位置查找及按值查找实战5. 往第i个位置插入元素实战6. 链表的调试方法总结234561. 与408关联解析及本节内容介…

面对无法投入模型训练的object类型数据在头疼,快来使用我的丝滑小连招

面对无法投入模型训练的object类型数据在头疼&#xff0c;快来使用我的丝滑小连招 前言 丝滑小连招 tip1- get_dummies完美one-hot&#xff08;str->int&#xff09; tip2 - rename_dims解决重名问题&#xff01; tip3 - insert且drop&#xff01;​​​​​​​ 前言 我…

小爱同学控制美的美居中的家电热水器,空调等

背景 家里大多数家电都是支持接入米家App的&#xff0c;美的家电不能接入小米&#xff0c;电脑安装Home Assistant成功实现小爱语音控制美的燃气热水器。 实现步骤&#xff1a; 1. 安装docker 我的电脑是windows的&#xff0c;那就直接安装docker desktop https://desktop.…