代码训练LeetCode(6)编辑距离

代码训练(6)LeetCode之编辑距离

Author: Once Day Date: 2024年3月9日

漫漫长路,才刚刚开始…

全系列文章可参考专栏: 十年代码训练_Once-Day的博客-CSDN博客

参考文章:

  • 72. 编辑距离 - 力扣(LeetCode)
  • 力扣 (LeetCode) 全球极客挚爱的技术成长平台

文章目录

      • 代码训练(6)LeetCode之编辑距离
        • 1. 原题
        • 2. 分析
        • 3. 代码实现
        • 4. 总结

1. 原题

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

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

  • 插入一个字符
  • 删除一个字符
  • 替换一个字符

例如对于horseros两个单词,其最少操作数为3,即如下三步:

horse -> rorse (将 'h' 替换为 'r')
rorse -> rose (删除 'r')
rose -> ros (删除 'e')
2. 分析

这种表面一看,似乎是个字符串问题,但是如果按照分类匹配去做,怕是很难得出合理的方法。求两个字符串的编辑距离实际是个动态规划入门题目,动态规划算法是解决这个问题的标准方法。

我们先从逻辑分析一下,对于两个字符串,如horseros,在三种操作下,其最小操作数有下述四种情况:

  1. 已知字符串horsros的最小操作数,然后再删除一个字母e,即: MinOperation["hors"]["ros"] + 1
  2. 已知字符串horsero的最小操作数,然后再增加一个字母s,即: MinOperation["horse"]["ro"] + 1
  3. 已知字符串horsro的最小操作数,然后再替换一个字母e -> s,即: MinOperation["hors"]["ro"] + 1
  4. 已知字符串horsros的最小操作数,如果最后一个字母相同,则不变,即: MinOperation["hors"]["ros"]

第四种情况为特殊情况,即无需替换,通过上面分类讨论思想,可以发现,MinOperation["horse"]["ros"]取决于其单词前面字符的最小操作,这点很好理解,因为最后一个操作,一定是上述四种操作之一。

我们用i代表horsei个字符组成的子字符串,j代表rosj个字符组成的子字符串,则存在下述表达式:

MinOperation[i][j] = Min(
	(MinOperation[i-1][j] + 1), 
	(MinOperation[i][j-1] + 1), 
    (MinOperation[i-1][j-1] + 1(如果不相等)))

不断递归迭代下去,我们只要确定边界条件,则可按照递推关系求解任意ij值的最小操作数,如下:

  1. i = 0时,即MinOperation[0][j] = j,因为word1是空字符串,直接增加j个字符后变成word2。
  2. j = 0时,即MinOperation[i][0] = i,因为word2是空字符串,直接删除i个字符后变成word2。
  3. i = j = 0时,即MinOperation[0][0] = 0,都是空字符串。

下面创建一个二维数组 dp,其中 dp[i][j] 表示从 word1 的前 i 个字符转换到 word2 的前 j 个字符所需要的最小操作数。

  1. 初始化边界条件:dp[i][0]dp[0][j] 分别表示将 word1 的前 i 个字符全部删除和将 word2 的前 j 个字符全部插入到 word1
  2. 遍历word1word2的每个字符:
    • 如果当前字符相同,则 dp[i][j] = dp[i-1][j-1]
    • 如果字符不同,我们需要考虑三种情况:
      • 插入一个字符:dp[i][j] = dp[i][j-1] + 1
      • 删除一个字符:dp[i][j] = dp[i-1][j] + 1
      • 替换一个字符:dp[i][j] = dp[i-1][j-1] + 1
    • 我们取这三种情况的最小值作为 dp[i][j] 的结果。
  3. 最后 dp[length(word1)][length(word2)] 就是我们要找的答案。

按照这个算法我们可以逐步算出不同ij值的最小操作数,如下表所示:

(1) 首先构建初始化边界条件,即ij都为0的情况。

MinOperation(空字符串0)字符1r字符2o字符3s
(空字符串0)0123
字符1h1
字符2o2
字符3r3
字符4s4
字符5e5

(2) 然后我们可以按照从左到右,从上到下,依次遍历整个表格,直到填满整个表格。

MinOperation(空字符串0)字符1r字符2o字符3s
(空字符串0)0123
字符1h11(替换h)23
字符2o221(o相等)2
字符3r322(删除r)2
字符4s4332(s相等)
字符5e5443(删除e)

通过上表可以看出来,依次遍历整张表格的流程,也就揭示了操作的流程。

本次问题的性能优化关键点如下:

  • 空间优化:如果我们只关心最终的编辑距离,不需要回溯操作路径,则可以只保留 dp 数组的两行来节省空间。
  • 代码优化:确保循环和逻辑判断尽可能简洁,避免不必要的计算。
3. 代码实现
int minDistance(char * word1, char * word2){
    int len1 = strlen(word1);
    int len2 = strlen(word2);
    int dp[len1 + 1][len2 + 1];

    if (len1 == 0) {
        return len2;
    }
    if (len2 == 0) {
        return len1;
    }
    
    for (int i = 0; i <= len1; i++) {
        dp[i][0] = i;
    }
    for (int j = 0; j <= len2; j++) {
        dp[0][j] = j;
    }

    for (int i = 1; i <= len1; i++) {
        for (int j = 1; j <= len2; j++) {
            if (word1[i - 1] == word2[j - 1]) {
                dp[i][j] = dp[i - 1][j - 1];
            } else {
                dp[i][j] = 1 + (dp[i - 1][j - 1] < dp[i][j - 1] ?
                                (dp[i - 1][j - 1] < dp[i - 1][j] ? dp[i - 1][j - 1] : dp[i - 1][j]) :
                                (dp[i][j - 1] < dp[i - 1][j]? dp[i][j - 1] : dp[i - 1][j]));
            }
        }
    }

    return dp[len1][len2];
}

上面的 C 语言代码实现了编辑距离算法:

  • minDistance 函数用于计算并返回编辑距离。
  • len1len2 分别是两个输入单词的长度。
  • dp 是一个二维数组,用于存储到当前位置为止的最小编辑距离。
  • 初始化 dp 数组的第一行和第一列,这表示一个字符串转换成空字符串所需的步骤数。
  • 接下来的双层 for 循环用于填充 dp 数组的剩余部分。
  • 我们使用嵌套的三元运算符来找到插入、删除和替换操作中的最小值。
  • 函数最后返回 dp[len1][len2],即将 word1 转换为 word2 所需的最小操作数。

下面是运行结果图(数据仅供参考,不具备实际意义):

在这里插入图片描述

4. 总结

本题主要考察了动态规划的理解和应用能力。动态规划是一个非常强大的工具,它可以解决许多看似复杂的问题,将问题分解成一系列子问题,并且保证每个子问题只解决一次,保存其解以避免重复计算。

要提高解决这类问题的能力,我们应该:

  • 理解动态规划的基本概念,如状态转移方程、边界条件等。
  • 练习各种不同类型的动态规划问题,从简单到复杂逐渐提升。
  • 学会如何将问题分解为子问题,并识别出子问题之间的依赖关系。
    的问题,将问题分解成一系列子问题,并且保证每个子问题只解决一次,保存其解以避免重复计算。






Alt

Once Day

也信美人终作土,不堪幽梦太匆匆......

如果这篇文章为您带来了帮助或启发,不妨点个赞👍和关注,再加上一个小小的收藏⭐!

(。◕‿◕。)感谢您的阅读与支持~~~

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

44岁「台偶一哥」成现实版「王子变青蛙」,育一子一女成人生赢家

电影《周处除三害》近日热度极高&#xff0c;男主角阮经天被大赞演技出色&#xff0c;最让人意想不到&#xff0c;因为该片在内地票房报捷&#xff0c;很多人走去恭喜另一位台湾男艺人明道&#xff0c;皆因二人出道时外貌神似&#xff0c;至今仍有不少人将两人搞混。 多年过去&…

RNN预测正弦时间点

import torch.nn as nn import torch import numpy as np import matplotlib matplotlib.use(TkAgg) from matplotlib import pyplot as plt # net nn.RNN(100,10) #100个单词&#xff0c;每个单词10个维度 # print(net._parameters.keys()) #序列时间点预测num_time_steps 50…

什么是RabbitMQ的死信队列

RabbitMQ的死信队列&#xff0c;是一种用于处理消息&#xff0c;处理失败或无法路由的消息的机制。它允许将无法被正常消费的消息重新路由到另一个队列&#xff0c;以便稍后进行进一步的处理&#xff0c;分析或排查问题。 当消息队列里面的消息出现以下几种情况时&#xff0c;就…

ChatGPT 控制机器人的基本框架

过去的一年&#xff0c;OpenAI的chatGPT将自然语言的大型语言模型&#xff08;LLM&#xff09;推向了公众的视野&#xff0c;人工智能AI如一夜春风吹遍了巴黎&#xff0c;全世界都为AI而疯狂。 OpenAI ChatGPT是一个使用人类反馈进行微调的预训练生成文本模型。不像以前的模型主…

每日一练:LeeCode-209、长度最小的子数组【滑动窗口+双指针】

每日一练&#xff1a;LeeCode-209、长度最小的子数组【滑动窗口双指针】 思路暴⼒解法滑动窗口 本文是力扣 每日一练&#xff1a;LeeCode-209、长度最小的子数组【滑动窗口双指针】 学习与理解过程&#xff0c;本文仅做学习之用&#xff0c;对本题感兴趣的小伙伴可以出门左拐 L…

《剑指 Offer》专项突破版 - 面试题 76 : 数组中第 k 大的数字(C++ 实现)

目录 详解快速排序 面试题 76 : 数组中第 k 大的数字 详解快速排序 快速排序是一种非常高效的算法&#xff0c;从其名字可以看出这种排序算法最大的特点是快。当表现良好时&#xff0c;快速排序的速度比其他主要对手&#xff08;如归并排序&#xff09;快 2 ~ 3 倍。 快速排…

力扣---腐烂的橘子

题目&#xff1a; bfs思路&#xff1a; 感觉bfs还是很容易想到的&#xff0c;首先定义一个双端队列&#xff08;队列也是可以的~&#xff09;&#xff0c;如果值为2&#xff0c;则入队列&#xff0c;我这里将队列中的元素定义为pair<int,int>。第一个int记录在数组中的位…

嵌入式驱动学习第二周——使用perf进行性能优化

前言 这篇博客来聊一聊如何使用perf进行性能优化。 嵌入式驱动学习专栏将详细记录博主学习驱动的详细过程&#xff0c;未来预计四个月将高强度更新本专栏&#xff0c;喜欢的可以关注本博主并订阅本专栏&#xff0c;一起讨论一起学习。现在关注就是老粉啦&#xff01; 目录 前言…

考研复习C语言初阶(4)+标记和BFS展开的扫雷游戏

目录 1. 一维数组的创建和初始化。 1.1 数组的创建 1.2 数组的初始化 1.3 一维数组的使用 1.4 一维数组在内存中的存储 2. 二维数组的创建和初始化 2.1 二维数组的创建 2.2 二维数组的初始化 2.3 二维数组的使用 2.4 二维数组在内存中的存储 3. 数组越界 4. 冒泡…

HarmonyOS NEXT应用开发之MpChart图表实现案例

介绍 MpChart是一个包含各种类型图表的图表库&#xff0c;主要用于业务数据汇总&#xff0c;例如销售数据走势图&#xff0c;股价走势图等场景中使用&#xff0c;方便开发者快速实现图表UI。本示例主要介绍如何使用三方库MpChart实现柱状图UI效果。如堆叠数据类型显示&#xf…

FPGA的配置状态字寄存器Status Register

目录 简介 状态字定义 Unknown Device/Many Unknow Devices 解决办法 一般原因 简介 Xilinx的FPGA有多种配置接口&#xff0c;如SPI&#xff0c;BPI&#xff0c;SeletMAP&#xff0c;Serial&#xff0c;JTAG等&#xff1b;如果从时钟发送者的角度分&#xff0c;还可以…

2024/3/10周报

文章目录 摘要Abstract文献阅读题目问题创新点方法Section1&#xff1a;运动员检测Section2&#xff1a;行为识别输入层隐藏层输出层 实验实验数据评估指标模型设置实验结果 深度学习模糊逻辑系统概念模糊化模糊规则解模糊 总结 摘要 本周阅读了一篇关于基于YOLO和深度模糊LST…

131.分割回文串

// 定义一个名为Solution的类 class Solution {// 声明一个成员变量&#xff0c;用于存储所有满足条件的字符串子序列划分结果List<List<String>> lists new ArrayList<>(); // 声明一个成员变量&#xff0c;使用LinkedList实现的双端队列&#xff0c;用于临…

【Objective -- C】—— 自引用计数

【Objective -- C】—— 自引用计数 一. 内存管理/自引用计数1.自引用计数2.内存管理的思考方式自己生成的对象&#xff0c;自己持有非自己生成的对象&#xff0c;自己也能持有不再需要自己持有的对象时释放无法释放非自己持有的对象 3.alloc/retain/release/dealloc实现4. aut…

力扣--滑动窗口438.找到字符串中所有字母异位词

思路分析&#xff1a; 使用两个数组snum和pnum分别记录字符串s和p中各字符出现的次数。遍历字符串p&#xff0c;统计其中各字符的出现次数&#xff0c;存储在pnum数组中。初始化snum数组&#xff0c;统计s的前m-1个字符的出现次数。从第m个字符开始遍历s&#xff0c;通过滑动窗…

《YOLO5Face: Why Reinventing a Face Detector》为什么要重塑人脸检测器论文阅读

正好周末的时间天气也不错出去走走精神不错&#xff0c;回来读一篇论文这个论文之前查资料的时候看到的但是没有完整看下&#xff0c;今天正好花点时间整体看一下&#xff0c;下面是我自己阅读过程中使用翻译软件结合自己理解的阅读记录&#xff0c;感兴趣的话可以看下&#xf…

知识图谱 | 2023年图书馆学、情报学CSSCI期刊论文主题透视

数据来源 检索平台来源期刊年份有效数据中国知网大学图书馆学报国家图书馆学刊情报科学情报理论与实践情报学报情报杂志情报资料工作数据分析与知识发现图书馆建设图书馆论坛图书馆学研究图书馆杂志图书情报工作图书情报知识图书与情报现代情报信息资源管理学报中国图书馆学报2…

性能测试高阶内容:了解TPS和RT之间关系

引言 在开始今天的内容讲解之前&#xff0c;我们应该回顾一下&#xff0c;在我的全链路压测专栏中的第一篇&#xff0c;我就已经介绍了当前的性能测试在互联网企业中的重要性&#xff0c;已经性能在互联网行业中的占比是多少。 这个时候是不是会有同学问我&#xff0c; 你已经…

JVM-1

目录 1.基础知识 1.栈 2.本地方法栈 3.程序计数器 4.堆 5.方法区 6.JVM内存可见性 2.虚拟机类加载机制 1.加载 2.验证 3.准备 4.解析 5.初始化 6.使用 7.卸载 1.基础知识 JVM内存模型&#xff08;5种&#xff09;&#xff1a;栈&#xff0c;本地方法栈&#xff…

Java项目:44 ssm003在线医疗服务系统+jsp(含文档)

作者主页&#xff1a;舒克日记 简介&#xff1a;Java领域优质创作者、Java项目、学习资料、技术互助 文中获取源码 项目介绍 主要功能 前台登录&#xff1a; 注册用户&#xff1a;用户名、密码、姓名、联系电话 注册医生&#xff1a;医生工号、密码、医生姓名、职称、联系电话…