“文本比对基础:最短编辑距离算法的原理与实现“

最短编辑距离

给定两个字符串 𝐴 和 𝐵,现在要将 𝐴 经过若干操作变为 𝐵,可进行的操作有:

  1. 删除--将字符串 𝐴 中的某个字符删除。
  2. 插入--在字符串 𝐴 的某个位置插入某个字符。
  3. 替换--将字符串 𝐴 中的某个字符替换为另一个字符。

现在请你求出,将 𝐴 变为 𝐵 至少需要进行多少次操作。

输入格式

第一行包含整数 𝑛,表示字符串 𝐴 的长度。

第二行包含一个长度为 𝑛 的字符串 𝐴。

第三行包含整数 𝑚,表示字符串 𝐵 的长度。

第四行包含一个长度为 𝑚 的字符串 𝐵。

字符串中均只包含大小写字母。

输出格式

输出一个整数,表示最少操作次数。

数据范围

1≤𝑛,𝑚≤1000

输入样例:
复制代码
10
AGTCTGACGC
11
AGTAAGTAGGC
输出样例:
复制代码
4

1.状态表示 :fij

集合:将a1\~i 变成 b1\~j的操作方式

属性:min

2.状态计算 :从最后一步考虑

有三种操作,所以有三个子集

ok子集划分完了

考虑状态转移的时候

先考虑如果我没有进行这个操作应该是什么状态

然后考虑你进行这一步操作之后会对你下一个状态造成什么影响

然后再加上之前状态表示中你决策出来的那个DP属性

这样就可以自然而然地搞出来转移方程啦

1)删除操作:把ai删掉之后a1~i和b1~j匹配

所以之前要先做到a1~(i-1)和b1~j匹配

fi-1j + 1

2)插入操作:插入之后ai与bj完全匹配,所以插入的就是bj

那填之前a1~i和b1~(j-1)匹配

fij-1 + 1

3)替换操作:把ai改成bj之后想要a1~i与b1~j匹配

那么修改这一位之前,a1~(i-1)应该与b1~(j-1)匹配

fi-1j-1 + 1

但是如果本来ai与bj这一位上就相等,那么不用改,即

fi-1j-1 + 0

好的那么fij就由以上三个可能状态转移过来,取个min
细节问题:初始化怎么搞

先考虑有哪些初始化嘛

1.你看看在for遍历的时候需要用到的但是你事先没有的

(往往就是什么0啊1啊之类的)就要预处理

2.如果要找min的话别忘了INF

要找有负数的max的话别忘了-INF

ok对应的:

1.f0i如果a初始长度就是0,那么只能用插入操作让它变成b

fi0同样地,如果b的长度是0,那么a只能用删除操作让它变成b

2.fij = INF //虽说这里没有用到,但是把考虑到的边界都写上还是保险

思路:

f[i][j]表示第一个字符串的前 i 个字母变为第二个字符串的前 j 个字母所用的最少操作次数。

假设第一个字符串为:AGTCTGACGC

第二个字符串为:AGTAAGTAGGC

f[3][5] 表示把第一个字符串的前三个字母变为第二个字符串的前五个字母所需要的最少操作次数。

也就是把AGT变为AGTAA所用的最少操作次数。

分析递推方程:

把第一个字符串的前 i 个字母变为第二个字符串的前 j 个字母,有三种方法:

  • 把第一个字符串的前 i 个字母变为第二个字符串的前 j - 1 个字符,然后在第一个字符串后面增加第二个字符串的第j个字母。

    这种情况下,f[i][j] = f[i][j - 1] + 1

    例如:把AGT变为AGTAA,可以先把AGT变为AGTA,然后再在最后面添加一个字符A。

  • 把第一个字符串的前 i - 1 个字母变为第二个字符串的前 j 个字符,然后去掉最后一个字符。

​ 例如:把AGT变为AGTAA,可以先把AGT变为AGTAAT,然后再去掉一个字符T。

​ 这种情况下,f[i][j] = f[i - 1][j] + 1

  • 把第一个字符串的前 i - 1 个字母变为第二个字符串的前 j - 1个字符。变化之后,对比最后一个字符,如果相等,则变换完成,如果不同,把第一个字符串的最后一个字符变为第二个字符串的最后一个字符,

    例如:把AGT变为AGTAA,可以先把AGT变为AGTAT,然后再在最后面添加一个字符T变为A。

    例如:把AGT变为AGTAT,可以先把AGT变为AGTAT,因为最后一个字符相同,不再做处理。

    这种情况下,f[i][j] = f[i - 1][j - 1] + 1(最一个字符不同) 或f[i][j] = f[i - 1][j + 1] (最一个字符相同)。

取三种情况的最小值,就是f[i][j]

时间复杂度:O(n^2)。

代码:

cpp 复制代码
#include <iostream>
using namespace std;
const int N = 1010;

char a[N], b[N];//分别存放两个字符串
int f[N][N];//存储结果

int n, m;//两个字符串的长度

int main()
{
    cin >> n;
    for(int i = 1; i <= n; i++) cin >> a[i];

    cin >> m;
    for(int i = 1; i <= m; i++) cin >> b[i];
    //边界,a 字符串的前 i 个字符变为 b 字符串的前 0 个字符,需要 i 步
    for(int i = 0; i <= n; i++) f[i][0] = i;
    //边界,a 字符串的前 0 个字符变为 b 字符串的前 i 个字符,需要 i 步
    for(int i = 0; i <= m; i++) f[0][i] = i;
    
    for(int i = 1; i <= n; i++)
    {
        for(int j = 1; j <=m; j++)
        {
            //情况一 二
            f[i][j] = min(f[i][j - 1] + 1, f[i - 1][j] + 1);
            //情况三
            if(a[i] != b[j]) f[i][j] = min(f[i][j], f[i - 1][j - 1] + 1);
            else f[i][j] = min(f[i][j], f[i - 1][j - 1]);
        }
    }
    //f[n][m] 表示 a 字符传递额前 n 个字符变为 b 字符传递额前 m 个字符需要的最少步骤。
    cout << f[n][m];

}
相关推荐
kkeeper~2 小时前
0基础C语言积跬步之数据在内存中的存储
c语言·数据结构·算法
wabs6664 小时前
关于贪心算法的一些自我总结【力扣45.跳跃游戏II】【灵感来源:代码随想录】
算法·贪心算法·复盘
2401_876964134 小时前
【湖北专升本】2026湖北专升本真题PDF+备考资料汇总
数据结构·人工智能·经验分享·深度学习·算法·计算机视觉
嗝o゚4 小时前
CANN GE 算子融合——融合算法与调度策略
算法·昇腾·cann·ge
小江的记录本5 小时前
【JVM虚拟机】垃圾回收GC:垃圾回收算法:标记-清除、标记-复制、标记-整理、分代收集(附《思维导图》+《面试高频考点清单》)
java·jvm·后端·python·算法·安全·面试
Ulyanov6 小时前
用声明式语法重新定义Python桌面UI:QML+PySide6现代开发入门(一)
开发语言·python·算法·ui·系统仿真·雷达电子对抗仿真
数据科学小丫6 小时前
特征工程处理
人工智能·算法·机器学习
z落落7 小时前
C#参数区别
java·算法·c#
c238567 小时前
vector(下)
数据结构·算法
z落落7 小时前
C# 冒泡排序+选择排序 + Array.Sort 自定义排序
数据结构·算法