问题描述
为了将一个文本串
x
[
1
⋯
m
]
x\begin{bmatrix}1 \cdots m \\\end{bmatrix}
x[1⋯m]转换为目标串
y
[
1
⋯
n
]
y\begin{bmatrix}1 \cdots n \\\end{bmatrix}
y[1⋯n],我们可以使用多种变换操作。我们的目标是,给定
x
x
x和
y
y
y,求将
x
x
x转换为
y
y
y的一个变换操作序列。我们使用一个数组
z
z
z保存中间结果,假定它足够大,可存下中间结果的所有字符。初始时,&z&是空的;结束时,应有
z
[
j
]
=
y
[
j
]
z [ j ] = y [ j ]
z[j]=y[j],
j
=
1
,
2
,
…
,
n
j = 1 , 2 , … , n
j=1,2,…,n。我们维护两个下标
i
i
i和
j
j
j,分别指向
x
x
x和
y
y
y中的位置,变换操作允许改变
z
z
z的内容和这两个下标。初始时,
i
=
j
=
1
i = j = 1
i=j=1。在转换过程中应处理
x
x
x的所有字符,这意味着在变换操作结束时,应当有
i
=
m
+
1
i = m + 1
i=m+1。
我们可以使用如下6种变换操作:
复制(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]。
替换(replace) — 将
x
x
x的当前字符
x
[
i
]
x[i]
x[i]替换为另一个字符
c
c
c,然后进行赋值
z
[
j
]
=
c
z [ j ] = c
z[j]=c,并将两个下标i ii和j jj都增1。此操作处理了
x
[
i
]
x [ i ]
x[i]。
删除(delete) — 删除
x
x
x中的当前字符
x
[
i
]
x[i]
x[i],即将
i
i
i增1,
j
j
j不变。此操作处理了
x
[
i
]
x[i]
x[i]。
插入(insert) — 将字符
c
c
c插入
z
z
z中,
z
[
j
]
=
c
z[j]=c
z[j]=c,将
j
j
j增1,
i
i
i不变。此操作未处理
x
x
x中的字符。
旋转(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]。
终止(kill) — 删除
x
x
x中的剩余字符,令
i
=
m
+
1
i=m+1
i=m+1。此操作处理了
x
x
x中所有尚未处理的字符。如果执行此操作,则转换过程结束。
每个变换操作都有相应的代价。具体的代价依赖于特定的应用,但我们假定每个操作的代价是一个已知的常量。我们还假定复制和替换的代价小于删除和插入的组合代价,否则复制和替换操作就没有意义了。一个给定的变换操作序列的代价为其中所有变换操作的代价之和。
算法实现
使用自底向上的动态规划来解决这个问题,最优的代价由前序代价+操作代价得出。
//author:Yiwantang
//EditionDistance.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include <string>
#include <vector>
#include <limits.h>
void Print_Operator_Chain(std::vector<std::vector<int> > op ,int i, int j,int p)
{
if ((i == 0 && j == 0)||i<0||j<0)
{
return;
}
else if (op[i][j] == 0)
{
Print_Operator_Chain(op, i-1, j-1,p);
std::cout << "Copy " << i << " to " << j << std::endl;
}
else if (op[i][j] == 1)
{
Print_Operator_Chain(op, i-1, j-1,p);
std::cout << "Replace " << i << " to " << j << std::endl;
}
else if (op[i][j] == 2)
{
Print_Operator_Chain(op, i-1, j,p);
std::cout << "Delete " << i << " to " << j << std::endl;
}
else if (op[i][j] == 3)
{
Print_Operator_Chain(op, i, j-1,p);
std::cout << "Insert " << i << " to " << j << std::endl;
}
else if (op[i][j] == 4)
{
Print_Operator_Chain(op, i-2, j-2,p);
std::cout << "Twiddle " << i << " to " << j << std::endl;
}
else if (op[i][j] == 5)
{
Print_Operator_Chain(op, p, j,p);
std::cout << "kill " << p << std::endl;
}
else
{
return;
}
}
int Edit_Distance()
{
using std::string;
int cost[6] = { 0,1,2,2,0,0 };
int d[6];
int p;
std::cout << "Please input two strings:" << std::endl;
string x, y;
std::cin >> x >> y;
int x_length = x.length();
int y_length = y.length();
using std::vector;
vector<vector<int> > c(x_length + 1, vector<int>(y_length + 1, 0));
vector<vector<int> > op(x_length + 1, vector<int>(y_length + 1, 0));
for (int i = 0; i <= x_length; i++)
{
c[i][0] = i;
op[i][0] = 2;
}
for (int i = 0; i <= y_length; i++)
{
c[0][i] = i;
op[0][i] = 3;
}
if (y_length == 0 && (cost[5] < c[x_length][0]))
{
c[x_length][0] = cost[5];
op[x_length][0] = 5;
p = 0;
}
for (int i = 1; i <= x_length; i++)
for (int j = 1; j <= y_length; j++)
{
for (int k = 0; k < 6; k++)
d[k] = INT_MAX;
if (x[i-1] == y[j-1])
{
d[0] = cost[0] + c[i - 1][j - 1];
}
else
{
d[1] = cost[1] + c[i - 1][j - 1];
}
d[2] = cost[2] + c[i - 1][j];
d[3] = cost[3] + c[i][j - 1];
if (i >= 2 && j >= 2 && x[i - 2] == y[j-1] && x[i-1] == y[j - 2])
d[4] = cost[4] + c[i - 2][j - 2];
if (i == x_length && j == y_length)
{
for (int k = 0; k < x_length; k++)
if (cost[5] + c[k][y_length] < d[5])
{
d[5] = cost[5] + c[k][y_length];
p = k;
}
}
c[i][j] = INT_MAX;
for (int k = 0; k < 6; k++)
{
if (d[k] < c[i][j])
{
c[i][j] = d[k];
op[i][j] = k;
}
}
}
for (int i = 0; i <= x_length; i++) {
for (int j = 0; j <= y_length; j++)
{
std::cout << c[i][j] << " ";
}
std::cout << std::endl;
}
Print_Operator_Chain(op, x_length, y_length, p);
return 0;
}
int main()
{
Edit_Distance();
return 0;
}