【动态规划】求解编辑距离问题

news2024/12/26 22:09:24

目录

  • 问题描述
  • 递推关系
  • 运行实例
  • 时空复杂度优化
  • Hirschberg 算法

问题描述

编辑距离问题是求解将⼀个字符串转换为另⼀个字符串所需的插⼊、删除、替换的最小次数。 C O M M O M → s u b C O M M U M → s u b C O M M U N → i n s C O M M U N E \mathbb{COMMOM} \overset{sub}{\rightarrow} \mathbb{COMMUM} \overset{sub}{\rightarrow}\mathbb{COMMUN} \overset{ins}{\rightarrow} \mathbb{COMMUNE} COMMOMsubCOMMUMsubCOMMUNinsCOMMUNE上述将单词 COMMOM 变为 COMMUNE 共需要经过至少3次操作。

对编辑距离进行可视化,可以得到序列比对

COMMOM-
COMMUNE
  • 第一行的空格表示插入
  • 第二行的空格表示删除
  • 具有不同字符的列表示替换

编辑距离 = 序列比对中具有不同字符的列数

最小编辑距离 = 最优序列比对中具有不同字符的列数

编辑距离问题也可以这么表述:
对于给定字符串 A [ 1... m ] A[1...m] A[1...m] B [ 1... n ] B[1...n] B[1...n] 求解他们的最小编辑距离 D ( m , n ) D(m,n) D(m,n)


递推关系

假设对 ∀ i < m , ∀ j < n \forall i<m,\forall j<n i<m,j<n,可以计算 A [ 1... i ] A[1...i] A[1...i] B [ 1... j ] B[1...j] B[1...j]的最小编辑距离 D ( i , j ) D(i,j) D(i,j)

COMMOM-
COMMUNE

考虑 A [ 1... m ] A[1...m] A[1...m] B [ 1... n ] B[1...n] B[1...n] 的最优比对,可以发现如下规律:

  1. 最后⼀列不可能是两个空格
  2. 某个串为空串时,最小编辑距离是另⼀个串的长度
  3. A [ m ] A[m] A[m] B [ n ] B[n] B[n] 都存在: D ( m , n ) = D ( m − 1 , n − 1 ) + ( A [ m ] = B [ n ] ? 0 : 1 ) D(m,n) =D(m − 1,n − 1) + (A[m] = B[n]?0 : 1) D(m,n)=D(m1,n1)+(A[m]=B[n]?0:1)
  4. A [ m ] A[m] A[m] B [ n ] B[n] B[n] 有一方为空,删除不为空的那一个: D ( m , n ) = { D ( m − 1 , n ) + 1 A [ m ] a n d − D ( m , n − 1 ) + 1 B [ n ] a n d − D(m,n) = \begin{cases} D(m − 1,n) + 1 & A[m]\quad and \quad- \\ D(m, n − 1) + 1 & B[n]\quad and \quad- \\ \end{cases} D(m,n)={D(m1,n)+1D(m,n1)+1A[m]andB[n]and
  5. 综上,只需要沿着三条路径递归得到最小的那条 D ( m , n ) = { i i f j = 0 j i f i = 0 min ⁡ { D ( m − 1 , n ) + 1 D ( m , n − 1 ) + 1 D ( m − 1 , n − 1 ) + ( A [ m ] = B [ n ] ? 0 : 1 ) o t h e r w i s e D(m,n) = \begin{cases} i &if\quad j=0\\ j &if\quad i=0 \\ \min \begin{cases} D(m − 1,n) + 1 \\ D(m, n − 1) + 1 \\ D(m − 1,n − 1) + (A[m] = B[n]?0 : 1) \end{cases} &otherwise \end{cases} D(m,n)= ijmin D(m1,n)+1D(m,n1)+1D(m1,n1)+(A[m]=B[n]?0:1)ifj=0ifi=0otherwise
  6. 时间复杂度: O ( m n ) O(mn) O(mn) ; 空间复杂度: O ( m n ) O(mn) O(mn)

运行实例

在这里插入图片描述
对于每个 D [ i , j ] D[i,j] D[i,j], 都可以通过 D [ i − 1 , j − 1 ] D[i-1,j-1] D[i1,j1]; D [ i − 1 , j ] D[i-1,j] D[i1,j]; D [ i , j − 1 ] D[i,j-1] D[i,j1]这三个点得到而这三个点又分别对应 替换;删除;插入三种操作。

通过上述递推关系,我们可以自上而下,自左向右构造记录表。在填完记录表后右下角的那个值即为最小编辑距离。接着就是使用回溯的方式,构造满足最小编辑距离的最优比对(如下图右侧所示)
在这里插入图片描述

#include <iostream>
#include <algorithm>
#include <vector>
#include <string>

using namespace std;

// 计算最小编辑距离,并返回最小编辑距离的值,计算编辑距离表dp
int minEditDistance(const string& word1, const string& word2, vector<vector<int>>& dp) {
    int m = word1.length();
    int n = word2.length();

    for (int i = 0; i <= m; ++i) {
        for (int j = 0; j <= n; ++j) {
            if (i == 0) {
                dp[i][j] = j;
            }
            else if (j == 0) {
                dp[i][j] = i;
            }
            else if (word1[i - 1] == word2[j - 1]) {
                dp[i][j] = dp[i - 1][j - 1];
            }
            else {
                dp[i][j] = 1 + min({ dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1] });
            }
        }
    }

    return dp[m][n];
}

// 通过回溯法找到所有满足最小编辑距离的操作序列。
void findAllSequences(const string& word1, const string& word2, int i, int j, const string& sequence, vector<string>& sequences, vector<vector<int>>& dp) {
    if (i == 0 && j == 0) {
        sequences.push_back(sequence);
        return;
    }

    if (i > 0 && j > 0 && word1[i - 1] == word2[j - 1]) {
        findAllSequences(word1, word2, i - 1, j - 1, "No operation: " + string(1, word1[i - 1]) + " -> " + string(1, word2[j - 1]) + "\n" + sequence, sequences, dp);
    }

    if (i > 0 && j > 0 && dp[i][j] == dp[i - 1][j - 1] + 1) {
        findAllSequences(word1, word2, i - 1, j - 1, "Replace: " + string(1, word1[i - 1]) + " -> " + string(1, word2[j - 1]) + "\n" + sequence, sequences, dp);
    }

    if (i > 0 && dp[i][j] == dp[i - 1][j] + 1) {
        findAllSequences(word1, word2, i - 1, j, "Delete: " + string(1, word1[i - 1]) + " \n" + sequence, sequences, dp);
    }

    if (j > 0 && dp[i][j] == dp[i][j - 1] + 1) {
        findAllSequences(word1, word2, i, j - 1, "Insert: " + string(1, word2[j - 1]) + " \n" + sequence, sequences, dp);
    }
}

int main() {
    string word1 = "ALTRUISTIC";
    string word2 = "ALGORITHM";

    vector<vector<int>> dp(word1.length() + 1, vector<int>(word2.length() + 1, 0));

    int minDistance = minEditDistance(word1, word2, dp);

    cout << "Minimum Edit Distance between " << word1 << " and " << word2 << " is: " << minDistance << endl;

    vector<string> sequences;
    findAllSequences(word1, word2, word1.length(), word2.length(), "", sequences, dp);

    cout << "Operations to convert " << word1 << " to " << word2 << " are: " << endl;
    for (const string& seq : sequences) {
        cout << seq << "----------"<< endl;
    }

    return 0;
}

运行结果:

在这里插入图片描述


时空复杂度优化

现在我们已经可以计算最小编辑距离,同时构造出最优比对。他们的时空复杂度总结如下:

计算最小编辑距离构造最优比对
时间 O ( m n ) O(mn) O(mn) O ( m + n ) O(m+n) O(m+n)
空间 O ( m n ) O(mn) O(mn) O ( m n ) O(mn) O(mn)

从实际情况来看, O ( m n ) O(mn) O(mn) 的空间比 O ( m n ) O(mn) O(mn) 的时间更难满足,比如当 m = n = 1 0 5 m = n = 10^5 m=n=105

  • 时间上:执行 1 0 10 10^{10} 1010次指令大约需要10秒(假设CPU每秒执行 1 0 9 10^9 109条指令)
  • 空间上:需要 1 0 10 10^{10} 1010bits,大约 40 GB

那么能否使用 O ( m + n ) O(m+n) O(m+n) 的空间来构造最优比对呢?

答:可以使用 Hirschberg 算法。


Hirschberg 算法

Hirschberg 算法是一种高效的线性空间动态规划算法。它通过使用分治策略来降低空间复杂度,从而在线性空间内计算最优比对。

该算法的思想基于以下洞察力:

  • 在动态规划算法中,通常使用二维矩阵来存储中间状态,这导致了 O ( m n ) O(mn) O(mn) 的空间复杂度。
  • 但实际上,可以通过观察计算过程中的对称性,将动态规划的空间复杂度降低到 O ( m + n ) O(m+n) O(m+n)

在这里插入图片描述
在计算动态规划的过程中,我们观察到 D ( i , j ) D(i,j) D(i,j) 的计算仅依赖于 D ( i − 1 , j ) D(i-1,j) D(i1,j) D ( i , j − 1 ) D(i,j-1) D(i,j1) D ( i − 1 , j − 1 ) D(i-1,j-1) D(i1,j1)。基于此,我们可以利用两个长度为 n 的一维数组来存储中间状态,每次只需要保留上一行和当前行的信息。

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

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

相关文章

迪杰斯特拉算法(C++)

目录 介绍&#xff1a; 代码&#xff1a; 结果&#xff1a; 介绍&#xff1a; 迪杰斯特拉算法&#xff08;Dijkstras algorithm&#xff09;是一种用于计算加权图的单点最短路径的算法。它是由荷兰计算机科学家Edsger W. Dijkstra在1956年发明的。 该算法的思路是&#xf…

Confluence 快速安装教程

安装jdk yum install -y java-1.8.0-openjdk.x86_64 java -version 安装MySQL mkdir -p /data/mysql/data chmod 777 /data/mysql/datadocker rm -f mysql docker run -d --name mysql \-p 3306:3306 \-e MYSQL_ROOT_PASSWORDfingard1 \-v /data/mysql/data:/var/lib/mysql …

使用Pandas进行时间重采样,充分挖掘数据价值

大家好&#xff0c;时间序列数据蕴含着很大价值&#xff0c;通过重采样技术可以提升原始数据的表现形式。本文将介绍数据重采样方法和工具&#xff0c;提升数据可视化技巧。 在进行时间数据可视化时&#xff0c;数据重采样是至关重要且非常有用的&#xff0c;它支持控制数据的…

SQL 文本函数

前言 SQL文本函数是SQL语言中非常有用的一类函数&#xff0c;它们用于处理和操作字符串数据。在实际应用中&#xff0c;我们经常需要对数据库中的文本数据进行各种操作&#xff0c;比如提取子串、替换子串、拼接字符串等等。而SQL文本函数可以帮助我们轻松地完成这些任务&#…

SQL SERVER 2008安装教程

SQL SERVER 2008安装教程 本篇文章介绍了安装SQL Server 2008企业版的软硬件配置要求&#xff0c;安装过程的详细步骤&#xff0c;以及需要注意的事项。 安装步骤 (1). 在安装文件setup.exe上&#xff0c;单击鼠标右键选择“以管理员的身份运行”&#xff0c;如下图所示&#…

一文搞懂设计模式之代理模式

大家好&#xff0c;我是晴天&#xff0c;本周我们又见面了。本周有点发烧感冒&#xff0c;更文有点慢了&#xff0c;请大家见谅。言归正传&#xff0c;本周我们继续一起学习一文搞懂设计模式系列文章之代理模式。 什么是代理模式 我们先来看看 GoF 对代理模式的定义&#xff1…

HarmonyOS真机调试报错:INSTALL_PARSE_FAILED_USESDK_ERROR处理

1、 新建应用时选择与自己真机匹配的sdk版本 查看自己设备sdk版本 创建时先择匹配版本&#xff1a; 2、 根据报错提示连接打开处理方案 3、查询真机版本对应的compileSdkVersion 和 compatibleSdkVersion 提示3.1版本之后和3.1版本之前的不同命令&#xff08;此处为3.0版…

Git企业开发级讲解(四)

&#x1f4d8;北尘_&#xff1a;个人主页 &#x1f30e;个人专栏:《Linux操作系统》《经典算法试题 》《C》 《数据结构与算法》 ☀️走在路上&#xff0c;不忘来时的初心 文章目录 一、理解分⽀二、创建分支三、切换分⽀四、合并分⽀五、删除分⽀六、合并冲突七、分⽀管理策略…

轻量封装WebGPU渲染系统示例<34>-数据驱动之Json构建场景

场景和数据之间的互通&#xff1a; 场景数据化或者数据化场景&#xff0c;是当前的主流场景数据构成方式。方便传输方便交换甚至是交互。 内置数据互通机制更有利于用户在各种应用场合下实现具体的3D相关的应用需求。用户只需要关心标准的或者约定好的数据定义及操作方式就能方…

PostgreSQL 入门

文章目录 PostgreSQL介绍PostgreSQL和MySQL的区别PostgreSQL的安装PostgreSQL的配置远程连接配置配置数据库的日志 PostgreSQL基本操作用户操作权限操作 图形化界面安装总结 PostgreSQL介绍 PostgreSQL是一个功能强大的 开源 的关系型数据库&#xff0c;底层基于C实现。其开源…

Sam Altman 被罢免细节曝光,投资 100+ 公司或成「话柄」?

2022 年 11 月&#xff0c;ChatGPT 发布掀起 AI 狂潮。时隔 1 年&#xff0c;2023 年 11 月&#xff0c;ChatGPT 之父、Sam Altman 的一项人事巨变&#xff0c;再次掀起了一场 AI 界的风暴&#xff0c;只是这次并不是技术革命&#xff0c;而是 OpenAI 巨头换帅——Sam Altman 被…

YOLO目标检测——烟雾检测数据集下载分享【含对应voc、coco和yolo三种格式标签】

实际项目应用&#xff1a;烟雾检测数据集可用于监控烟雾情况&#xff0c;实现火灾的早期预警。数据集说明&#xff1a;烟雾检测数据集&#xff0c;真实场景的高质量图片数据&#xff0c;数据场景丰富&#xff0c;含烟雾1个类别标签说明&#xff1a;使用lableimg标注软件标注&am…

QtCreator开发环境的安装和配置

QtCreator开发环境的安装和配置 介绍下载与安装环境介绍示例新建工程示例程序 帮助模式Qt Designer(设计师)Qt Linguist(预言家)结论 介绍 Qt Creator是一个跨平台、完整的集成开发环境&#xff08;IDE&#xff09;&#xff0c;专门用于Qt开发。它包含了完整的编辑器、调试器和…

数据结构 栈与队列详解!!

一.栈 关于内存中的栈和数据结构中的栈是不同的&#xff0c;本章着重讲的是数据结构的栈。 这是一张关于栈的表达图。从图中可以看出栈很像是一副卡牌&#xff0c;发牌时只能从上取出&#xff0c;即出栈。 而入栈则是像你出牌后&#xff0c;要把你出的牌压在上一张出的牌上面。…

Android跨进程通信,IPC,RPC,Binder系统,C语言应用层调用

文章目录 Android跨进程通信&#xff0c;IPC&#xff0c;RPC&#xff0c;Binder系统&#xff0c;C语言应用层调用&#xff08;&#xff09;1.概念2.流程3.bctest.c3.1 注册服务&#xff0c;打开binder驱动3.2 获取服务 4.binder_call Android跨进程通信&#xff0c;IPC&#xf…

Swagger示例

对于项目完成后不用写文档,好处还是蛮大的 不需要关注项目其他 只关注接口与实体类即可 SpringBoot项目 依赖 <!--Swagger依赖--> <dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger2</artifactId><version…

Vue3 插槽 v-slot

插槽 视频链接&#xff1a;尚硅谷vue-插槽章节 不使用插槽的情况下 结果&#xff1a; 1 默认插槽 结果&#xff1a; 2 具名插槽 #b是v-slot:b 的缩写 顾名思义就是指着名字去插入 结果&#xff1a; 3 作用域插槽 可以传递数据的插槽&#xff0c;子组件可以将数据回…

​软考-高级-系统架构设计师教程(清华第2版)【第15章 面向服务架构设计理论与实践(P527~554)-思维导图】​

软考-高级-系统架构设计师教程&#xff08;清华第2版&#xff09;【第15章 面向服务架构设计理论与实践&#xff08;P527~554&#xff09;-思维导图】 课本里章节里所有蓝色字体的思维导图

经典ctf ping题目详解 青少年CTF-WEB-PingMe02

题目环境&#xff1a; 根据题目名称可知 这是一道CTF-WEB方向常考的知识点&#xff1a;ping地址 随便ping一个地址查看接受的数据包?ip0.0.0.0 有回显数据&#xff0c;尝试列出目录文件 堆叠命令使用’;作为命令之间的连接符&#xff0c;当上一个命令完成后&#xff0c;继续执…

【数字人】7、GeneFace++ | 使用声音驱动的面部运动系数作为 condition 来指导 NeRF 重建说话头

文章目录 一、背景二、相关工作2.1 唇形同步的 audio-to-motion2.2 真实人像渲染 三、方法3.1 对 GeneFace 的继承3.2 GeneFace 的结构3.2.1 Pitch-Aware Audio-to-Motion Transform3.2.2 Landmark Locally Linear Embedding3.2.3 Instant Motion-to-Video Rendering 四、效果 …