题目:
假设,字符串仅有三个基本操作:删除一个字符、插入一个字符和将一个字符修改成另一个字符。
基本的字符操作:进行了一次上述三种操作的任意一种操作
两个字符串的编辑距离的定义:通过上述的基本操作,我们可以把字符串a变成字符串b,所需要的最少基本字符操作次数
举例:snowy 与 sunny 的编辑距离为3
我们的任务:计算任意两个字符串的编辑距离
状态转移方程
d[ i ][ j ]:表示把源文本串X的前i个字符串转换为目标串Y前j个字符串的最小代价
d[ i ][ j ] = min (d[i - 1][j - 1] + cost , d[i - 1][j]+1, d[i][j-1]+1)
其中,当 X[i] = Y[j]时,cost = 0。当 X[i] != Y[j],cost = 1
注意:这三种情况都可以转化成d[ i ][ j ],它们是并列的,写状态转移方程时,所有能转化为d[ i ][ j ]的情况都要考虑到。
简单说明:如果取d[i][j - 1]最小,表示X[1 ~ i] 和 Y[1 ~ j - 1] 已经完全匹配,由于X的i个序列已经全部处理完毕,但是Y还剩一个,我们只需要在往X中添加一个元素Y[j]即可,此时X的前i个和Y的前j个就全部相等了
迭代代码
#include <iostream>
using namespace std;
const int MaxLen = 20;
//char X[] = " snowy";
//char Y[] = " sunny";
char X[] = " interestingly";
char Y[] = " bioinformatics";
int d[MaxLen][MaxLen] = {{0}};
void PrintD(int lenX,int lenY)
{
for (int i = 0;i <= lenX;i++)
{
for (int j = 0;j <= lenY;j++)
{
cout<<d[i][j]<<" ";
}
cout<<endl;
}
}
int Min(int x,int y,int z)
{
if (x < y)
{
if (x < z) // x < y
{
return x;
}
else // z < x < y
{
return z;
}
}
else
{
if (y < z) //x > y
{
return y;
}
else //x > y y >= z
{
return z;
}
}
}
// 处理过程中,我们只修改原始字符串,对该字符串增删改
int EditDistance(int lenX,int lenY)
{
int cost = 0;
//初始化边界条件
//X串有内容,Y串没有内容
for (int i = 1;i <= lenX;i++)
{
d[i][0] = i;//X串要变成Y串,每次需要在X串中删除一个字符
}
//X串为空,Y有内容
for (int j = 1;j <= lenY;j++)
{
d[0][j] = j;//X串没内容,X串要变成Y,必须往X串中插入Y[i]
}
for (int i = 1;i <= lenX;i++)
{
for (int j = 1;j <= lenY;j++)
{
if (X[i] == Y[j])
{
cost = 0;
}
else
{
cost = 1;
}
int Replace = d[i - 1][j - 1] + cost;
int Insert = d[i][j - 1] + 1;
int Del = d[i - 1][j] + 1;
d[i][j] = Min(Replace,Insert,Del);
}
}
return d[lenX][lenY];
}
int main()
{
int lenX = strlen(X) - 1;
int lenY = strlen(Y) - 1;
cout<<EditDistance(lenX,lenY)<<endl;
//PrintD(lenX,lenY);
system("pause");
return 0;
}
注意一点:上述代码中,无论X[i] 与 Y[j]是否相等,都是进行了删除和添加操作。其实也可以在字符不等时,进行修改删除和添加操作。在字符相等时,不进行任何操作。d[i][j] = d[i - 1][j - 1].
备忘录
#include <iostream>
using namespace std;
const int MaxLen = 20;
/*char X[] = " interestingly";
char Y[] = " bioinformatics";*/
char X[] = " snowy";
char Y[] = " sunny";
int d[MaxLen][MaxLen] = {{0}};
int Min(int x,int y,int z)
{
if (x < y)
{
if (x < z) // x < y
{
return x;
}
else // z < x < y
{
return z;
}
}
else
{
if (y < z) //x > y
{
return y;
}
else //x > y y >= z
{
return z;
}
}
}
// 处理过程中,我们只修改原始字符串,对该字符串增删改
int EditDistance(int i,int j)
{
int cost = 0;
if (d[i][j])
{
return d[i][j];
}
//边界条件
if (i == 0 && j != 0)
{
return d[i][j] = EditDistance(i,j - 1) + 1;//插入代价
}
if (i != 0 && j == 0)
{
return d[i][j] = EditDistance(i - 1,j) + 1;//删除代价
}
if (!i && !j)
{
return 0;
}
//正式处理
if (X[i] == Y[j])
{
cost = 0;
}
else
{
cost = 1;
}
int Replace = EditDistance(i - 1,j - 1) + cost;
int Insert = EditDistance(i,j - 1) + 1;
int Del = EditDistance(i - 1,j) + 1;
return d[i][j] = Min(Replace,Insert,Del);
}
int main()
{
cout<<EditDistance(strlen(X) - 1,strlen(Y) - 1)<<endl;
system("pause");
return 0;
}
递归暴力
#include <iostream>
using namespace std;
const int MaxLen = 20;
//char X[] = " interestingly";
//char Y[] = " bioinformatics";
char X[] = " snowy";
char Y[] = " sunny";
int Min(int x,int y,int z)
{
if (x < y)
{
if (x < z) // x < y
{
return x;
}
else // z < x < y
{
return z;
}
}
else
{
if (y < z) //x > y
{
return y;
}
else //x > y y >= z
{
return z;
}
}
}
// 处理过程中,我们只修改原始字符串,对该字符串增删改
int EditDistance(int i,int j)
{
int cost = 0;
//边界条件
if (i == 0 && j != 0)
{
return j;//插入代价
}
if (i != 0 && j == 0)
{
return i;//删除代价
}
if (!i && !j)
{
return 0;
}
//正式处理
if (X[i] == Y[j])
{
cost = 0;
}
else
{
cost = 1;
}
int Replace = EditDistance(i - 1,j - 1) + cost;
int Insert = EditDistance(i,j - 1) + 1;
int Del = EditDistance(i - 1,j) + 1;
return Min(Replace,Insert,Del);
}
int main()
{
cout<<EditDistance(strlen(X) - 1,strlen(Y) - 1)<<endl;
system("pause");
return 0;
}