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

最短编辑距离

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

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

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

输入格式

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

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

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

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

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

输出格式

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

数据范围

1≤𝑛,𝑚≤1000

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

1.状态表示 :f[i][j]

集合:将a[1~i] 变成 b[1~j]的操作方式

属性:min

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

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

ok子集划分完了

考虑状态转移的时候

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

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

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

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

1)删除操作:把a[i]删掉之后a[1i]和b[1j]匹配

所以之前要先做到a[1(i-1)]和b[1j]匹配

f[i-1][j] + 1

2)插入操作:插入之后a[i]与b[j]完全匹配,所以插入的就是b[j]

那填之前a[1i]和b[1(j-1)]匹配

f[i][j-1] + 1

3)替换操作:把a[i]改成b[j]之后想要a[1i]与b[1j]匹配

那么修改这一位之前,a[1(i-1)]应该与b[1(j-1)]匹配

f[i-1][j-1] + 1

但是如果本来a[i]与b[j]这一位上就相等,那么不用改,即

f[i-1][j-1] + 0

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

先考虑有哪些初始化嘛

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

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

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

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

ok对应的:

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

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

2.f[i][j] = 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];

}
相关推荐
王中阳Go4 小时前
从超市收银到航空调度:贪心算法如何破解生活中的最优决策谜题?
java·后端·算法
故事挺秃然5 小时前
中文分词:机械分词算法详解与实践总结
算法·nlp
车队老哥记录生活7 小时前
【MPC】模型预测控制笔记 (3):无约束输出反馈MPC
笔记·算法
地平线开发者8 小时前
BEV 感知算法评价指标简介
算法·自动驾驶
不过四级不改名6778 小时前
用c语言实现简易c语言扫雷游戏
c语言·算法·游戏
C++ 老炮儿的技术栈10 小时前
手动实现strcpy
c语言·开发语言·c++·算法·visual studio
倔强的石头_10 小时前
【数据结构与算法】利用堆结构高效解决TopK问题
后端·算法
倔强的石头_10 小时前
【数据结构与算法】详解二叉树下:实践篇————通过链式结构深入理解并实现二叉树
后端·算法
哎写bug的程序员10 小时前
leetcode复盘(1)
算法·leetcode·职场和发展
风靡晚11 小时前
用于汽车毫米波雷达的四维高分辨率点云图像
人工智能·算法·机器学习·计算机视觉·汽车·信息与通信·信号处理