动态规划Dynamic programming详解-编辑距离问题【python】

news2024/11/26 0:53:55

作者介绍:10年大厂数据\经营分析经验,现任大厂数据部门负责人。
会一些的技术:数据分析、算法、SQL、大数据相关、python
欢迎加入社区:码上找工作
作者专栏每日更新:
LeetCode解锁1000题: 打怪升级之旅
python数据分析可视化:企业实战案例
备注说明:方便大家阅读,统一使用python,带必要注释,公众号 数据分析螺丝钉 一起打怪升级

动态规划是解决各种优化问题的强大工具,特别是在问题可以分解为重叠的子问题时。接下来,我将介绍一个流行的动态规划案例——编辑距离问题(又称Levenshtein距离)

1. 问题介绍和应用场景

编辑距离问题是一个经典的问题,用于量化两个字符串之间的差异,即将一个字符串转换成另一个字符串所需的最小编辑操作次数,包括插入、删除和替换字符。编辑距离广泛应用于自然语言处理、文本相似度检测、拼写检查、生物信息学等领域。

2. 问题定义和示例

定义:给定两个字符串 s1s2,计算将 s1 转换成 s2 所需的最少操作数。

示例

  • s1 = "horse"
  • s2 = "ros"

最少操作数为 3(删除 ‘h’,替换 ‘o’ 为 ‘r’,删除 ‘e’)。
在编辑距离问题中,状态转移方程是用来描述如何从较小问题的解构建较大问题的解的数学表达式。它核心地指示了在动态规划表中如何更新每个单元格的值。以下是详细的推导和解释。

3.状态转移方程

状态定义

dp[i][j] 为从字符串 s1 的前 i 个字符转换到 s2 的前 j 个字符所需的最小编辑操作数。这里,s1[0..i-1]s2[0..j-1] 分别表示字符串 s1s2 的前 ij 个字符。

状态转移方程的推导

考虑以下几种情况:

  1. 字符匹配(s1[i-1] == s2[j-1]:

    • 如果当前两个字符相同,那么这一对字符不需要任何编辑操作。因此,当前问题的解可以直接继承前一个问题的解,即:
      [ dp[i][j] = dp[i-1][j-1] ]
    • 这表示我们不对这对字符进行任何编辑,直接继承左上角单元格的编辑操作数。
  2. 字符不匹配(s1[i-1] != s2[j-1]:

    • 如果当前两个字符不相同,我们有三种策略来使得 s1[0..i-1] 转换为 s2[0..j-1]
      • 删除操作:从 s1 中删除一个字符后,尝试匹配 s1[0..i-2]s2[0..j-1],操作数为 dp[i-1][j] + 1
      • 插入操作:向 s1 中插入一个与 s2[j-1] 相同的字符,然后尝试匹配 s1[0..i-1]s2[0..j-2],操作数为 dp[i][j-1] + 1
      • 替换操作:将 s1[i-1] 替换为与 s2[j-1] 相同的字符,然后匹配 s1[0..i-2]s2[0..j-2],操作数为 dp[i-1][j-1] + 1
    • 综上,我们选择上述三种策略中的最小值:
      [ dp[i][j] = 1 + \min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]) ]

完整的状态转移方程

综合以上情况,编辑距离问题的状态转移方程可以表达为:

在这里插入图片描述

边界条件

  • 对于 i = 0 (即 s1 为空字符串时),将 s2 的前 j 个字符全部插入到 s1 是唯一的选项,因此 dp[0][j] = j
  • 对于 j = 0 (即 s2 为空字符串时),将 s1 的前 i 个字符全部删除是唯一的选项,因此 dp[i][0] = i

通过上述状态转移方程,我们可以系统地填充整个动态规划表,并最终解决编辑距离问题。这种方法不仅确保了解的正确性,而且通过避免冗余计算,提高了

4. 算法实现

def edit_distance(s1: str, s2: str) -> int:
    """
    计算两个字符串s1和s2之间的最小编辑距离。
    最小编辑距离是将s1转换成s2所需的最少单字符编辑操作次数(插入、删除、替换)。

    参数:
    s1 (str): 源字符串。
    s2 (str): 目标字符串。

    返回:
    int: s1转换成s2的最小编辑距离。
    """
    m, n = len(s1), len(s2)
    # 创建一个二维数组dp,大小为(m+1)x(n+1)
    dp = [[0] * (n + 1) for _ in range(m + 1)]

    # 初始化dp数组的第一行和第一列
    for i in range(m + 1):
        dp[i][0] = i  # 将s1的前i个字符删除
    for j in range(n + 1):
        dp[0][j] = j  # 将s2的前j个字符插入到s1中

    # 填充dp数组的其余部分
    for i in range(1, m + 1):
        for j in range(1, n + 1):
            if s1[i - 1] == s2[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]  # 返回将整个s1转换成s2的最小编辑距离

# 示例使用
s1 = "horse"
s2 = "ros"
print("最小编辑距离:", edit_distance(s1, s2))

复杂度分析

  • 时间复杂度:O(mn),因为需要填充一个 m x n 的矩阵。
  • 空间复杂度:O(mn),可以优化到 O(min(m, n)) 通过只保留当前和前一行的状态。
    为了清晰地解释编辑距离问题的动态规划解法,并辅以图解,我们可以使用一个示例和配套的表格。我们将采用简单的字符串 s1 = "horse"s2 = "ros" 来阐述过程。

算法图解

动态规划表初始化

创建一个表格,其中行表示字符串 s1 的前 i 个字符,列表示字符串 s2 的前 j 个字符。我们使用一个 (len(s1)+1) x (len(s2)+1) 的表格,len(s1)len(s2) 分别是两个字符串的长度。

初始化表格(第一行和第一列特殊处理)

初始表格结构如下:

ROS
0123
H1
O2
R3
S4
E5
  • 第一行第一列 的填充是基于单字符插入和删除操作的累积成本。例如,第一行第二格的 1 表示将空串变为 “R” 需要一次插入操作,依此类推。
填充过程

遍历 s1 的每个字符(行),与 s2 的每个字符(列)进行比较:

  1. H vs R:

    • 不匹配。取 min(左方, 上方, 左上方+1)min(1, 1, 1) = 1 (dp[1][1] 表示从"H"到"R"的最小编辑距离)
  2. H vs O:

    • 不匹配。min(1, 2, 2) = 2 (由 “H” 到 “RO” 或由 “HO” 到 “R”)
  3. H vs S:

    • 不匹配。min(2, 3, 3) = 3 (由 “H” 到 “ROS” 或由 “HS” 到 “RO”)
  4. O vs R:

    • 不匹配。min(2, 1, 2) = 2 (由 “O” 到 “R” 或由 “HO” 到 “RO”)
  5. O vs O:

    • 匹配。dp[1][1] + 0 = 1 (无操作, 因为 “O” 匹配 “O”)
  6. O vs S:

    • 不匹配。min(3, 2, 2) = 2 (由 “O” 到 “ROS” 或由 “HO” 到 “OS”)
  7. R vs R:

    • 匹配。dp[2][1] + 0 = 2 (无操作, 因为 “R” 匹配 “R”)
  8. R vs O:

    • 不匹配。min(2, 3, 3) = 3 (由 “HR” 到 “RO” 或由 “HOR” 到 “R”)
  9. R vs S:

    • 不匹配。min(3, 4, 3) = 3 (由 “HR” 到 “ROS” 或由 “HOR” 到 “OS”)
  10. Final Row (E):

    • 类似上述步骤,我们继续用 s1 的 “E” 与 s2 的每个字符进行比较填表。

继续填充表格的详细步骤:

ROS
0123
H1123
O2212
R3222
S4332
E5443
逐步分析与填充
  1. S vs R, O, S:

    • S vs R: 不匹配。min(左方, 上方, 左上方+1)min(3, 2, 3) = 2
    • S vs O: 不匹配。min(左方, 上方, 左上方+1)min(2, 3, 3) = 2
    • S vs S: 匹配。dp[4][3]dp[3][2] + 0 = 2
  2. E vs R, O, S:

    • E vs R: 不匹配。min(左方, 上方, 左上方+1)min(4, 3, 3) = 3
    • E vs O: 不匹配。min(左方, 上方, 左上方+1)min(3, 4, 4) = 3
    • E vs S: 不匹配。min(左方, 上方, 左上方+1)min(2, 3, 4) = 2
完整填充后的表格
ROS
0123
H1123
O2212
R3222
S4332
E5443

在最右下角的单元格 dp[5][3],我们得到 s1 = "horse"s2 = "ros" 的最小编辑距离为 3。这表明我们需要进行三次编辑操作(删除两个字符 ‘h’ 和 ‘e’,并将一个 ‘r’ 替换为 ‘o’)来将 “horse”
回溯过程转换成 “ros”。以下是更加具体的步骤和解释:

回溯过程

要详细了解如何从 “horse” 变成 “ros”,我们可以从填充完成的动态规划表格开始回溯。从表格的右下角 (dp[5][3]) 开始,每一步都选择了一个操作,直到我们回到表格的左上角 (dp[0][0])。

从表格回溯操作:

  1. 从 ‘e’ 到 ‘s’

    • 位置 (5, 3): 此时字符 ‘e’ (s1的第5个字符) 和 ‘s’ (s2的第3个字符) 不匹配。
    • 可以通过将 ‘e’ 替换为 ‘s’ 来减少一个不匹配,即 dp[4][2]。所以我们进行替换操作。
  2. 从 ‘s’ 到 ‘s’

    • 位置 (4, 2): 此时字符 ‘s’ 和 ‘s’ 匹配。
    • 由于字符匹配,我们直接沿对角线向上移动到 dp[3][1],无需额外操作。
  3. 从 ‘r’ 到 ‘o’

    • 位置 (3, 1): 此时字符 ‘r’ 和 ‘o’ 不匹配。
    • 可以通过将 ‘r’ 替换为 ‘o’ 来减少一个不匹配,即 dp[2][0]。所以我们进行替换操作。
  4. 从 ‘o’ 到 ‘’ (空)

    • 位置 (2, 0): 此时我们需要将 ‘o’ 删除,因为s2已无更多字符。
    • 沿着第一列向上,每次删除 s1 中的字符,直到 dp[1][0]
  5. 从 ‘h’ 到 ‘’ (空)

    • 位置 (1, 0): 最后,删除 ‘h’,完成所有转换。
结果

通过以上回溯步骤,我们可以确定将 “horse” 转换为 “ros” 的最小编辑操作序列是:

  • 替换 ‘e’ 为 ‘s’
  • 替换 ‘r’ 为 ‘o’
  • 删除 ‘h’
  • 删除 ‘e’

5.总结

编辑距离问题的动态规划解法提供了一个有效的方式来计算两个字符串的相似度。该问题不仅在理论计算中有着重要的地位,而且在许多实际应用中都有广泛的用途,如拼写纠正、DNA序列分析等。通过这种方法,我们可以准确地评估和处理字符串数据,支持复杂的数据分析和决策制定。

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

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

相关文章

【笔试训练】day3

今天的题又简单了很多欸 1.简写单词 没思路 代码&#xff1a; #include <iostream> #include<string> using namespace std;int main() {string str;string ans;getline(cin,str);if(str[0]>Z)ans(str[0]-32);else ansstr[0];for(int i1;i<str.size();i…

Linux第88步_非阻塞IO实验

非阻塞IO是“应用程序”对“驱动设备”进行操作&#xff0c;若不能获取到设备资源&#xff0c;则非阻塞IO应用程序的线程不会被“挂起”&#xff0c;即线程不进入休眠&#xff0c;而是一直“轮询”&#xff0c;直到获取到设备资源为止&#xff0c;或者直接放弃。 非阻塞IO应用举…

mybatis(9)-逆向工程+PageHelper+注解方式开发

最后一篇&#xff01;&#xff01; 1、逆向工程1.1、普通版1.2、增强版 2、PageHelper2.1 limit2.2 插件 3、注解开发3.1 Insert3.2Delete3.3 Update3.4 Select Results 1、逆向工程 1.1、普通版 所谓的逆向工程是&#xff1a;根据数据库表逆向生成Java的pojo类&#xff0c;S…

纯golang开发的mqtt server

Mochi-MQTT Server github地址&#xff1a;https://github.com/mochi-mqtt/server Mochi-MQTT 是一个完全兼容的、可嵌入的高性能 Go MQTT v5&#xff08;以及 v3.1.1&#xff09;中间件/服务器。 Mochi MQTT 是一个完全兼容 MQTT v5 的可嵌入的中间件/服务器&#xff0c;完…

YoloV9实战:从Labelme到训练、验证、测试、模块解析

模型实战 训练COCO数据集 本次使用2017版本的COCO数据集作为例子&#xff0c;演示如何使用YoloV8训练和预测。 下载数据集 Images: 2017 Train images [118K/18GB] &#xff1a;http://images.cocodataset.org/zips/train2017.zip2017 Val images [5K/1GB]&#xff1a;htt…

选择最佳 PoE 布线系统的 3 个步骤

选择合适的 POE 布线系统的重要性 在不断发展的信息和通信技术 &#xff08;ICT&#xff09; 领域&#xff0c;以太网供电 &#xff08;PoE&#xff09; 布线系统已成为一种革命性的解决方案&#xff0c;它简化了网络设备的部署和管理&#xff0c;同时优化了电力传输。从智能建…

傅里叶变换到底是什么

傅里叶变换到底是什么 有一个f&#xff08;t&#xff09;经傅里叶变换公式转化成F&#xff08;w&#xff09;&#xff1b; F&#xff08;w&#xff09;包括 欧拉公式转化成无限包括sin cos的函数相加。sin cos前面的参数a不为0说明这个周期函数分量存在&#xff0c;是某一种有…

基本模拟概念

目标&#xff1a; 讨论模拟电子技术的基本特性 描述模拟信号 分析信号源 解释放大器的特性 1.1模拟电子学 电子学可以划分成很多的分类来研究。其中最基本的一种分类方式是将信号分成可由 二进制数字表示的数字信号和由连续变化量表示的模拟信号。数字电子学包括所有的算术 和…

想做好抖音直播运营,这81个专业术语你必须得知道 沈阳新媒体运营培训

1.起号 释义&#xff1a;从0基础创建账号到1账号已具备基础模型&#xff0c;启动一个直播间&#xff0c;并使其能稳定卖出去东西的过程。简单一点来说&#xff0c;就是冷启动&#xff0c;通过活动或者其他方式获得快速大量曝光的一种形式。目前主流起号方式为&#xff1a;▼活…

【C++】unordered_set和unordered_map

底层哈希结构 namespace hash_bucket {template<class T>struct HashData{T _data;struct HashData* next nullptr;HashData(const T& data):_data(data){}};//仿函数:这里直接用开散列仿函数template <class K>struct HashFunc{size_t operator()(const K&a…

attention and tell论文【无标题】

这个公式使用LaTeX语法表示为&#xff1a; ( i t f t o t c t ) ( σ σ σ tanh ⁡ ) T D m n , n ( E y t − 1 h t − 1 x t ) \begin{pmatrix}i_t \\f_t \\o_t \\c_t\end{pmatrix} \begin{pmatrix}\sigma \\\sigma \\\sigma \\\tanh\end{pmatrix}T_{Dmn,n}\begin{pmatri…

HackMyVM-Gift

目录 信息收集 arp nmap WEB dirsearch hydra ssh连接 get root 信息收集 arp ┌─[rootparrot]─[~] └──╼ #arp-scan -l Interface: enp0s3, type: EN10MB, MAC: 08:00:27:16:3d:f8, IPv4: 192.168.9.102 Starting arp-scan 1.10.0 with 256 hosts (https://git…

动态规划算法求解最长公共子序列

动态规划算法是运筹学中求解多阶段决策问题的经典算法&#xff0c;本文将介绍动态规划算法的基本思想&#xff0c;并介绍如何使用动态规划算法求解最长公共子序列问题。 1. 动态规划算法的基本思想 动态规划算法本质也是基于分治思想&#xff0c;将待求解问题分解成若干个子问…

「GO基础」目录

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…

02_JavaWeb中的Tomcat(详解)

文章目录 Tomcat1, 概述1.1 安装1.2 目录结构1.3 启动/停止 2, 资源部署2.1 直接部署: 主要和重要的方式2.2 虚拟映射: 重要2.2.1 方式一:2.2.1 方式二: 2.3 原理解析 3, Tomcat组件3.1 Connector3.2 Engine3.2.1 Host3.2.1.1 Context 4, 其它: 重要4.1 设置 Tomcat 1, 概述 w…

(踩坑)Please refer to 异常和Error creating bean with name 异常

一、Please refer to 异常 如图所示&#xff0c;在使用maven构建项目的时候&#xff0c;如果提示该错误&#xff0c;则可能是xml配置文件有问题或者测试类等。但是没有明确的异常信息&#xff0c;所以做以下小改动&#xff0c;可以查看异常信息。 在IDEA工具中&#xff0c;打…

08 SQL进阶 -- 集合运算 -- 表的连结(JOIN)

1. 连结(JOIN) 前一节我们学习了 UNION和INTERSECT 等集合运算, 这些集合运算的特征就是以行方向为单位进行操作. 通俗地说, 就是进行这些集合运算时, 会导致记录行数的增减。使用 UNION 会增加记录行数,而使用 INTERSECT 或者 EXCEPT 会减少记录行数。 但这些运算不能改变…

【Java开发指南 | 第十篇】Java修饰符

读者可订阅专栏&#xff1a;Java开发指南 |【CSDN秋说】 文章目录 JAVA修饰符访问修饰符非访问修饰符static 修饰符final 修饰符abstract 修饰符synchronized 修饰符transient 修饰符volatile 修饰符 JAVA修饰符 修饰符用来定义类、方法或者变量&#xff0c;通常放在语句的最前…

2024年【高处安装、维护、拆除】考试题库及高处安装、维护、拆除复审模拟考试

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 高处安装、维护、拆除考试题库是安全生产模拟考试一点通生成的&#xff0c;高处安装、维护、拆除证模拟考试题库是根据高处安装、维护、拆除最新版教材汇编出高处安装、维护、拆除仿真模拟考试。2024年【高处安装、维…

瞬态瑞丽波频散曲线提取

频散曲线 function [Y1, f, phase] = das_fft(signal1, signal2, Ts) [y, lag