算法导论 编辑距离

编辑距离是动态规划算法学习的经典问题。

leetcode有一道面试常考的 编辑距离,算法导论上的编辑距离实验要比leetcode上的实验更加的难。

算法导论书编辑距离习题:

为了将一个文本串 x [ 1.. m ] x [ 1.. m ] x[1..m] 转换为目标串 y [ 1.. n ] y [ 1.. n ] y[1..n],我们可以使用多种变换操作。我们的目标是,给定 x x x y y y ,求将 x x x转换为 y y y的一个变换操作序列。我们使用一个数组 z z z 保存中间结果,假定它足够大,可存下中间结果的所有字符。初始时, z z z是空的;结束时,应有
z [ j ] = y [ j ] , j = 1 , 2 , … , n z [ j ] = y [ j ] , j = 1 , 2 , … , n z[j]=y[j]j=1,2,,n。我们维护两个下标i和j,分别指向 x x x y y y中的位置,变换操作允许改变z的内容和这两个下标。初始时, i = j = 1 i = j = 1 i=j=1 。在转换过程中应处理 x x x的所有字符,这意味着在变换操作结束时,应当有 i = m + 1 i = m + 1 i=m+1
  我们可以使用如下6种变换操作:
  1. 复制(copy) — 从 x x x复制当前字符到 z z z,即进行赋值 z [ j ] = x [ i ] z [ j ] = x [ i ] z[j]=x[i] ,并将两个下标 i i i j j j都增1。此操作处理了 x [ i ] x [ i ] x[i]
  2. 替换(replace) — 将 x x x的当前字符 x [ i ] x [ i ] x[i]替换为另一个字符 c c c,然后进行赋值
   z [ j ] = c z [ j ] = c z[j]=c,并将两个下标 i i i j j j都增1。此操作处理了 x [ i ] x [ i ] x[i]
  3. 删除(delete) — 删除x中的当前字符 x [ i ] x [ i ] x[i] ,即将 i i i增1, j j j 不变。此操作处理了 x [ i ] x [ i ] x[i]
  4. 插入(insert) — 将字符 c c c 插入 z z z中, z [ j ] = c z [ j ] = c z[j]=c ,将 j j j增1, i i i不变。此操作未处理x 中的字符。
  5. 旋转(twiddle,即交换) — 将 x x x中的当前字符和下一个字符复制到 z z z中,但交换顺序, z [ j ] = x [ i + 1 ] z [ j ] = x [ i + 1 ] z[j]=x[i+1] z [ j + 1 ] = x [ i ] z [ j + 1 ] = x [ i ] z[j+1]=x[i] ,将 i i i j j j都增2。此操作处理了 x [ i ] x [ i ] x[i] x [ i + 1 ] x [ i + 1 ] x[i+1]
  6. 终止(kill) — 删除 x x x中的剩余字符,令 i = m + 1 i = m + 1 i=m+1。此操作处理了 x x x中所有尚未处理的字符。如果执行此操作,则转换过程结束。

要求:

下面给出了将源字符串algorithm转换为目标字符串altruistic的一种变换操作序列,下划线指出执行一个变换操作后两个下标i和j的位置:

注意,还有其他方法将algorithm转换为altruistic
  每个变换操作都有相应的代价。具体的代价依赖于特定的应用,但我们假定每个操作的代价是一个已知的常量。我们还假定复制和替换的代价小于删除和插入的组合代价,否则复制和替换操作就没有意义了。一个给定的变换操作序列的代价为其中所有变换操作的代价之和。在上例中,将algorithm转换为altruistic的代价为
在这里插入图片描述

3 × c o s t ( c o p y ) + c o s t ( r e p l a c e ) + c o s t ( d e l e t e ) + 4 × c o s t ( i n s e r t ) + c o s t ( t w i d d l e ) + c o s t ( k i l l ) 3 \times cost(copy)+cost(replace)+cost(delete)+4 \times cost(insert)+cost(twiddle)+cost(kill) 3×cost(copy)+cost(replace)+cost(delete)+4×cost(insert)+cost(twiddle)+cost(kill)

程序实验:

采用DP[i][j]表示,source[1…i]转换到target[1…j]需要的编辑距离,同时用option[i][j]记录当前的最佳操作。

状态转移方程:
在这里插入图片描述

#include <iostream>
#include<vector>
using namespace std;

int EditDistance(string source, string target){

    enum COST{copy_cost=1, replace_cost=150, delete_cost=99, insert_cost=99, twiddle_cost=2, kill_cost=5};
    //printf("%d %d %d %d %d %d\n", copy_cost, replace_cost, delete_cost, insert_cost, twiddle_cost, kill_cost);
    int m=source.size(),n=target.size();
    int INF=99999999;
    vector<vector<int>> DP(m+1,vector<int>(n+1,INF));
    vector<vector<int>> options(m+1,vector<int>(n+1,-1));

    //init
    DP[0][0]=0;
    for(int j=1;j<=n;j++){
        DP[0][j]=j*insert_cost;options[0][j]=3;
    }
    for(int i=1;i<=m;i++){
        DP[i][0]=i*delete_cost;options[i][0]=2;
    }

    //dp
    int t;
    for(int i=1;i<=m;i++){
        for(int j=1;j<=n;j++){
            if(source[i-1]==target[j-1]){
                t=DP[i-1][j-1]+copy_cost;
                if(DP[i][j]>t) {DP[i][j]=t; options[i][j]=0;}
            }
            if(i>=2&&j>=2&&source[i-2]==target[j-1]&&source[i-1]==target[j-2]){
                t = DP[i-2][j-2]+twiddle_cost;
                if(DP[i][j]>t) {DP[i][j]=t; options[i][j]=4;}
            }

            t = DP[i-1][j-1]+replace_cost;
            if(DP[i][j]>t) {DP[i][j]=t; options[i][j]=1;}

            t = DP[i-1][j]+delete_cost;
            if(DP[i][j]>t) {DP[i][j]=t; options[i][j]=2;}

            t = DP[i][j-1]+insert_cost;
            if(DP[i][j]>t) {DP[i][j]=t; options[i][j]=3;}
        }
        for(int k=0;k<i;k++){
            t = DP[k][n] + kill_cost;
            if(DP[i][n]>t) {DP[i][n]=t; options[i][n]=5;}
        }
    }

    int p=m,q=n;
    vector<int> path;
    while(!(p==0&&q==0)){
        path.push_back(options[p][q]);
        if(options[p][q]==0) {p=p-1;q=q-1;}
        else if(options[p][q]==1) {p=p-1;q=q-1;}
        else if(options[p][q]==2) {p=p-1;q=q;}
        else if(options[p][q]==3) {p=p;q=q-1;}
        else if(options[p][q]==4) {p=p-2;q=q-2;}
        else if(options[p][q]==5) {
            while(options[p][q]==5) p--;
        }
    }

    //输出方案
    string s="";
    p=0,q=0;
    for(int i=path.size()-1;i>=0;i--){
        if(path[i]==0) {
            printf("copy");
            s+=target[q];
            p=p+1;q=q+1;
        }
        else if(path[i]==1){
            printf("replace");
            s+=target[q];
            p=p+1;q=q+1;
        }
        else if(path[i]==2){
            printf("delete");
            p=p+1;
        }
        else if(path[i]==3){
            printf("insert");
            s+=target[q];
            q=q+1;
        }
        else if(path[i]==4){
            printf("twiddle");
            s+=target.substr(q,2);
            p=p+2;q=q+2;
        }
        else if(path[i]==5){
            printf("kill");
        }
        printf(" %s\n",s.c_str());
    }

    return DP[m][n];
}

int main()
{
    string source = "algorithm";
	string target = "altruistic";
	int dis = EditDistance(source, target);
	printf("distance is %d\n", dis);
    return 0;
}

实验结果:

在这里插入图片描述

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值