编辑距离是动态规划算法学习的经典问题。
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;
}
实验结果: