两个字符串的编辑距离、相似度求解
概念
字符串的编辑距离,是指利用字符操作,把字符串A转换成字符串B所需要的最少操作数
。其中,字符操作包括以下3种:- 删除一个字符 delete a character
- 插入一个字符 insert a character
- 替换一个字符 replace a character
字符串的相似度则是指,在字符串的编辑距离+1的倒数。
解决思路
另dis[i][j]表示从字符串word1[i]到字符串word2[j]的编辑距离。
假设word1:michaelab;word2:michaelxy。
下面分别分析3种字符操作的编辑距离:
* 考虑添加操作,在word1[i]后插入word2[j]的字符,此处为’y’。操作完成后,word1为michaelaby,word2为michaelxy。
此时问题就转变为求michaelab到michaelx的最小编辑距离,即dis[i][j-1]。
即dis[i][j] = dis[i][j-1] + 1
* 考虑删除操作,在word1[i]后删除word1[i]的字符,即’b’。操作完成后,word1为michaela,word2为michaelxy
此时问题就转变为求michaela到michaelxy的最小编辑距离,即dis[i-1][j]。
即dis[i][j] = dis[i-1][j] + 1
* 考虑替换操作,将word1[i]处的字符’b’替换为word2[j]处的字符’y’。如果此时二者相等,则编辑距离不会增加。
此时问题:dis[i][j]=dis[i-1][j-1] + 1/0
*上述三者取使得dis[i][j]最小的操作,在进行逐字符比较操作后,
dis[len1][len2]即为所求*
此时可列出dis[i][j]的方程如下:
1. dis[i][j] = 0 i=0,j=0
2. dis[i][j] = i j = 0
3. dis[i][j] = j i = 0
4. dis[i][j] = min(dis[i][j-1] + 1,dis[i-1][j] + 1,dis[i-1][j-1] + 1/0)
i > 0,j > 0
代码
int findmin(int a, int b, int c)
{
int ret = ((a < b) ? a : b);
return ((ret < c) ? ret : c);
}
void geteditdist(const string& str1, const string& str2)
{
int len1 = str1.length();
int len2 = str2.length();
/*int** dis = new int* [len1+1];*/
int** dis = (int**)malloc(sizeof(int*)*(len1 + 1));
for (int i = 0; i <= len1; i++)
{
dis[i] = (int*)malloc(sizeof(int)*(len2 + 1));
/*dis[i] = new int[len2+1];*/
}
/*初始化数据*/
for (int i = 0; i <= len1; i++)dis[i][0] = i;
for (int j = 0; j <= len2; j++)dis[0][j] = j;
for (int i = 1; i <= len1; i++)
{
for (int j = 1; j <= len2; j++)
{
/*开始计算3种方法,然后比较哪个产生的编辑距离小*/
int add = dis[i][j - 1] + 1;
int del = dis[i - 1][j] + 1;
//判断dis[i][j]时需要判断str1[i-1]和str2[j-1]
//因为i和j的起始坐标都为1
int rep = dis[i - 1][j - 1] + ((str1[i-1] == str2[j-1]) ? 0 : 1);
dis[i][j] = findmin(add, del, rep);
}
}
cout < <dis[len1][len2] << endl;//编辑距离
cout << "1/"<< (dis[len1][len2]+1) << endl;//相似度
for (int i = 0; i <= len1; i++)
{
free(dis[i]);
}
free(dis);
}
int main(int argc, char* argv[])
{
string s1, s2;
while (cin >> s1 >> s2)
{
geteditdist(s1,s2);
}
return 0;
}