P2758 编辑距离 题解复盘

P2758 编辑距离 题解复盘

类型:字符串DP(编辑距离)

目标:将字符串 A 转换为字符串 B,使操作次数最少。


基本信息

项目 内容
题目编号、来源 P2758 洛谷
训练层级 B普及
知识版块 字符串DP、编辑距离、二维DP

解题前・关键信号识别

维度 分析
目标、约束、底层结构 将字符串 A 转换成字符串 B,允许删除、插入、修改三种操作,求最少操作次数。
数据规模 字符串长度 ≤2000,需要 O(n²) 算法。
候选算法和依据 每个状态都与两个字符串的前缀有关,使用二维DP。
复杂度预判 时间复杂度:O(nm) 空间复杂度:O(nm)

解题后・外化复盘

维度 内容
状态定义 dp[i][j] 表示:将字符串 A 的前 i 个字符转换成字符串 B 的前 j 个字符所需要的最少操作次数。
状态转移 若 a[i-1]==b[j-1]: dp[i][j]=dp[i-1][j-1] 否则: dp[i][j]=min(dp[i-1][j]+1, dp[i][j-1]+1, dp[i-1][j-1]+1)
遍历顺序 按 i、j 正序遍历,因为每个状态依赖左、上、左上三个状态。
实现结构 / 核心思路 ① 初始化第一行、第一列。 ② 双重循环枚举两个字符串前缀。 ③ 相同字符直接继承。 ④ 不同字符枚举删除、插入、修改三种操作。
错因回溯 ① 一开始倒序遍历,状态依赖方向错误。 ② 使用 a[i]、b[j],导致字符串越界,正确应使用 a[i-1]、b[j-1]。 ③ 初始化后仍从 (0,0) 开始更新,导致访问 dp[-1][*]。
边界和易错点 1. dp[i][0]=i(删除 i 次)。 2. dp[0][j]=j(插入 j 次)。 3. DP 从 (1,1) 开始。 4. 字符比较使用 a[i-1]、b[j-1]。
下次看到什么信号,我应该想到这个方法 看到: ① 字符串转换 ② 删除、插入、修改 ③ 求最少操作次数 想到:编辑距离(二维DP)。

三种操作理解

假设:

text 复制代码
A → B

始终是在修改第一个字符串 A。

删除

例如:

text 复制代码
abc

↓

ac

删除:

text 复制代码
b

对应:

cpp 复制代码
dp[i-1][j]+1

插入

例如:

text 复制代码
ab

↓

abc

插入:

text 复制代码
c

对应:

cpp 复制代码
dp[i][j-1]+1

修改

例如:

text 复制代码
cat

↓

cut

修改:

text 复制代码
a

↓

u

对应:

cpp 复制代码
dp[i-1][j-1]+1

为什么这样初始化?

第一列

例如:

text 复制代码
abc

↓

空串

只能:

text 复制代码
删除3次

所以:

cpp 复制代码
dp[3][0]=3;

即:

cpp 复制代码
dp[i][0]=i;

第一行

例如:

text 复制代码
空串

↓

abc

只能:

text 复制代码
插入3次

所以:

cpp 复制代码
dp[0][3]=3;

即:

cpp 复制代码
dp[0][j]=j;

为什么字符相同不用操作?

例如:

text 复制代码
abc

abc

最后一个字符:

text 复制代码
c
c

已经相同。

因此:

不用修改。

直接:

cpp 复制代码
dp[i][j]=dp[i-1][j-1];

AC完整代码

cpp 复制代码
#include<iostream>
#include<algorithm>
using namespace std;

int dp[2005][2005];

int main()
{
    string a,b;
    cin>>a>>b;

    int len1=a.size();
    int len2=b.size();

    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(a[i-1]==b[j-1])
            {
                dp[i][j]=dp[i-1][j-1];
            }
            else
            {
                dp[i][j]=min(
                    dp[i-1][j]+1,
                    min(
                        dp[i][j-1]+1,
                        dp[i-1][j-1]+1
                    )
                );
            }
        }
    }

    cout<<dp[len1][len2];

    return 0;
}

模型总结

模型 状态 转移
LCS dp[i][j] max()
编辑距离 dp[i][j] min()
路径DP dp[i][j] 从相邻位置转移
区间DP dp[l][r] 从左右区间转移

LCS 与 编辑距离 对比

内容 LCS 编辑距离
求什么 最长公共子序列长度 最少操作次数
字符相同 +1 不加
字符不同 max() min()
初始化 全部为0 第一行、第一列分别为0~n
状态 前缀最长公共子序列 前缀最少编辑次数

记忆

text 复制代码
看到:

① 字符串转换

② 删除

③ 插入

④ 修改

⑤ 求最少操作次数

↓

想到:

编辑距离

状态:

dp[i][j]

↓

字符相同:

dp[i-1][j-1]

↓

字符不同:

删除:

dp[i-1][j]+1

插入:

dp[i][j-1]+1

修改:

dp[i-1][j-1]+1

↓

取最小值
相关推荐
Tisfy10 小时前
LeetCode 2333.最小差值平方和:排序 + 数学计算(不用二分)
数学·算法·leetcode·题解·贪心·排序
aqiu11111110 小时前
【C++算法打怪专栏】LeetCode 217. 存在重复元素(解法总结)
c++·算法·leetcode
yi01111 小时前
DAY28: LeetCode 111. 二叉树的最小深度
人工智能·笔记·python·算法·leetcode·二叉树·递归
wuyk55511 小时前
27.LeetCode 2265. 统计值等于子树平均值的节点数 —— 后序遍历实战
stm32·单片机·嵌入式硬件·算法
xiangyun6111 小时前
C++ list 模拟实现(二):const迭代器与反向迭代器,模板的黑魔法
开发语言·数据库·c++·算法·stl·list
xxwl58512 小时前
三、区域赛算法模版第三章---字符串相关算法
笔记·学习·算法
信奥卷王12 小时前
[GESP202509 六级] 划分字符串(含视频)
数据结构·算法
酒醉的胡铁12 小时前
相似度搜索的常见算法
算法·ai
局部有解12 小时前
Leetcode Hot100 - 最长连续序列
算法·leetcode·职场和发展
行者全栈架构师13 小时前
给开源 Rust 工具加一个新检查项,TRAE 帮我从想法跑到构建产物:全链路实战
前端·算法·架构