【动态规划】编辑距离

题目大意&&问题分析

给定两个序列X{x1,x2,x3…}和Y{y1,y2,y3…},求从一个序列到另一个序列所需变换的最小次数,这里的变换包括删除,添加和替换,每次只对一个元素进行操作。

算法

这部分有参考《趣学算法》,也有很多自己的理解和思考,如有bug,欢迎批评指点。(鞠躬)

算法序言

这个题目有点像之前写的最长公共子序列问题,也是用动态规划的算法。能够应用动态规划的题目有一个特点就是具有最优子结构,就是整个问题的最优解会包含子问题的最优解,那么我们求出所有子问题的最优解,最后就可以得到整个问题的最优解。

算法核心

这个问题也是先求出{x1}和{y1},{y1,y2},…{y1,y2,y3…,yn},{x1,x2}和{y1},{y1,y2},…{y1,y2,y3…yn}的编辑距离,然后根据递推公式得到X和Y的编辑距离。
a,b:两个待比较序列;
a(i):表示取a序列前面长度为i的部分,b(j)同理;
i:序列a的下标;
j:序列b的下标;
c[i][j]:存储a[:i]和b[:j]的编辑距离;
dif:如果a[i]==b[j],则dif=0;否则dif=1;
其中最重要的还是递推公式:
c[i][j]=min{c[i][j-1]+1,c[i-1][j]+1,c[i-1][j-1]+dif};
下面来解释一下这个递推公式,对序列进行变换无非就是删除,增加和替换。 首先说删除,就是b中有这个元素但是a中没有,就要在a中删除,是在原来c[i-1][j]的基础上增加了一个删除操作变为c[i-1][j]+1,证明用反证法,s为a(i)和b(j)的编辑距离,假设s’=s-1不是a(i-1)和b(j)的编辑距离,那么a(i-1)和b(j)一定存在更小的编辑距离t’,在加上一个删除操作t=t’=1就是a(i)和b(j)的编辑距离,可知t<s,与s为a(i)和b(j)的编辑距离矛盾,所以假设不成立,那么在删除这种情况下c[i][j]=c[i-1][j]+1;
然后是增加,这个跟删除同理就是把下标表示变量变一下就可以,在这种情况下,c[i][j]=c[i][j-1]+1;
最后是替换,跟删除差不多,不过这里不是所有时候都+1,而是要看a[i]和b[j]是不是相同,如果a[i]和b[j]相同的话,c[i][j]=c[i-1][j-1];如果a[i]和b[j]不同的话,就是c[i][j]=c[i-1][j-1]+1,多了一个替换操作。
编辑距离是一个序列到另一个序列的最少操作次数,那么就要取这几种情况的最小值。

算法流程

先建立c[][]数组存储子问题的最优解,在初始化的时候要注意一下,和最长公共子序列问题是不一样的,这里是第一行的每一列都初始化为行号,第一列的每一行也是初始化为列号,可以这样理解,就是a序列为空的时候,另一个序列长度为多少就有多少的编辑距离。然后设双重循环用递推公式求子问题的最优解,也就是各个子序列的编辑距离,最后就可以得到两个完整序列的编辑距离。(如果想要得到究竟是怎么操作的,可以设一个数组记录每次操作,然后回溯即可;也可以不另设数组直接回溯,根据值来找到当前解的来源。)

代码实现

#include<iostream>
using namespace std;
const int maxn=105;

int c[maxn][maxn];//存放子问题的最优解
string a,b;
int m,n;//a,b的长度

int min_(int a,int b,int c)
{
    int temp;
    if(a>b)
        temp=b;
    else
        temp=a;
    if(temp>c)
        temp=c;
    return temp;
}

void EditDis()
{
    int i,j;
    for(i=0; i<=m; ++i) //初始化
        c[i][0]=i;
    for(j=0; j<=n; ++j)
        c[0][j]=j;
    for(i=1; i<=m; ++i)
        for(j=1; j<=n; ++j)
        {
            int dif;
            if(a[i]==b[j])//由最后当前位是否相等,来判断替换情况下的编辑距离
                dif=0;
            else
                dif=1;
            c[i][j]=min_(c[i][j-1]+1,c[i-1][j]+1,c[i-1][j-1]+dif);
        }
}

int main()
{
    cout<<"请输入第一个序列:";
    cin>>a;
    cout<<"请输入第二个序列:";
    cin>>b;//由a变成b的编辑距离,也可以理解为由b到a的编辑距离,操作倒过来即可
    m=a.size();
    n=b.size();
    EditDis();
    cout<<"两序列的编辑距离为:"<<c[m][n]<<endl;
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值