您的位置:首页 > 编程语言

跟着编程之美学算法——字符串相似度

2013-05-27 09:25 267 查看
计算字符串的相似度,这个问题是《编程之美》中的一个经典问题,问题的解决其实应该使用动态规划来解决,在《编程之美》中其实已经给出了该问题的子问题,不过最终用了递归的方法来实现,这使得算法有许许多多的重复计算,算法的复杂度达到了指数级。本文对该问题重新分析,用动态规划的方法来实现,是复杂度降为O(m*n)(其中m、n分别为两个字符串的长度)。

对于一个动态规划问题,那么解决这个问题的第一步就是找到子问题。这里利用《编程之美》中的分析方法:

不难看出,两个字符串的距离肯定不超过它们的长度之和(我们可以通过删除操作把两个串都转化为空串)。虽然这个结论对结果没有帮助,但至少可以知道,任意两个字符串的距离都是有限的。

我们还是就住集中考虑如何才能把这个问题转化成规模较小的同样的子问题。如果有两个串A=xabcdae和B=xfdfa,它们的第一个字符是相同的,只要计算A[2,...,7]=abcdae和B[2,...,5]=fdfa的距离就可以了。但是如果两个串的第一个字符不相同,那么可以进行如下的操作(lenA和lenB分别是A串和B串的长度)。

 1.删除A串的第一个字符,然后计算A[2,...,lenA]和B[1,...,lenB]的距离。

 2.删除B串的第一个字符,然后计算A[1,...,lenA]和B[2,...,lenB]的距离。

 3.修改A串的第一个字符为B串的第一个字符,然后计算A[2,...,lenA]和B[2,...,lenB]的距离。

 4.修改B串的第一个字符为A串的第一个字符,然后计算A[2,...,lenA]和B[2,...,lenB]的距离。

 5.增加B串的第一个字符到A串的第一个字符之前,然后计算A[1,...,lenA]和B[2,...,lenB]的距离。

 6.增加A串的第一个字符到B串的第一个字符之前,然后计算A[2,...,lenA]和B[1,...,lenB]的距离。

  在这个题目中,我们并不在乎两个字符串变得相等之后的字符串是怎样的。所以,可以将上面的6个操作合并为:

  1.一步操作之后,再将A[2,...,lenA]和B[1,...,lenB]变成相字符串。

  2.一步操作之后,再将A[2,...,lenA]和B[2,...,lenB]变成相字符串。

  3.一步操作之后,再将A[1,...,lenA]和B[2,...,lenB]变成相字符串。

总结:

如果两个字符串A,B,若第一个字母相同,那么,其距离d就为distance(A[2-n],B[2-m])。

若首字母不相同,那么距离d就为min(distance(A[1-n],B[2-m]), distance(A[2-n],B[1-m]), distance(A[2-n],B[2-m]))+1。

这样,很快就可以完成一个递归程序。这个程序也就是《编程之美》给我们的程序,如下:

#include <iostream>

using namespace std;

int Min(int a, int b, int c)
{
int min = a;
if(a > b)
min = b;
if(min > c)
min = c;
return min;
}

int GetDistance_DP(char *str1, char *str2)
{
if(str1 == NULL || str2 == NULL)
return 0;

int nLen1 = strlen(str1);
int nLen2 = strlen(str2);

if(nLen1 == 0)
return nLen2;
if(nLen2 == 0)
return nLen1;

int c[nLen1+1][nLen2+1];

for(int i = 0; i <= nLen1; i++)
c[i][0] = i;

for(int i = 0; i <= nLen2; i++)
c[0][i] = i;

for(int i = 0; i <= nLen1; i++)
{
for(int j = 0; j <= nLen2; j++)
{
if(str1[i] == str2[j])
{
c[i][j] = c[i-1][j-1];
}
else
{
c[i][j] = Min(c[i-1][j], c[i][j-1], c[i-1][j-1]) + 1;
}
}
}
int result = c[nLen1][nLen2];
return result;
}

int main()
{
cout<<GetDistance_DP("xabcdae", "xfdfa")<<endl;
return 0;
}


View Code

另类解法:

求两个字符串的编辑距离,也可以通过两个字符串的最长公共子序列的方法来求解:

首先求两个字符串的最长公共子序列,方法如:http://www.cnblogs.com/liyukuneed/archive/2013/05/22/3090597.html

比如要计算A、B两个字符串的编辑距离,同时已经得到A、B的最长公共子序列为len,A的长度为lenA,B的长度为lenB。

这样,A与B的编辑距离 = max(lenA - len, lenB - len)。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: