题目:
假设,字符串仅有三个基本操作:删除一个字符、插入一个字符和将一个字符修改成另一个字符。
基本的字符操作:进行了一次上述三种操作的任意一种操作
两个字符串的编辑距离的定义:通过上述的基本操作,我们可以把字符串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; }