计算字符串的类似度
很多程序会大量使用字符串。对于不同的字符串,我们希望可以有办法推断其相似程度。我们定义了一套操作方法来把两个不同样的字符串变得同样,详细的操作方法为:
1.改动一个字符(如把“a”替换为“b”)。
2.添加一个字符(如把“abdd”变为“aebdd”)。
3.删除一个字符(如把“travelling”变为“traveling”)。
比方,对于“abcdefg”和“abcdef”两个字符串来说,我们觉得能够通过添加/降低一个“g“的方式来达到目的。上面的两种方案,都仅须要一次操作。把这个操作所须要的次数定义为两个字符串的距离。给定随意两个字符串,你能否写出一个算法来计算出它们的距离?
分析与解法
不难看出,两个字符串的距离肯定不超过它们的长度之和(我们能够通过删除操作把两个串都转化为空串)。尽管这个结论对结果没有帮助,但至少能够知道。随意两个字符串的距离都是有限的。
我们还是应该集中考虑怎样才干把这个问题转化成规模较小的同样的问题。
假设有两个串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[1,…,lenA]和B[2,…,lenB]变成同样字符串。
3.一步操作之后。再将A[2,…,lenA]和B[2,…,lenB]变成同样字符串。
这样,非常快就能够完毕一个递归程序。
代码实现:
- int calStringDis(string strA, int pABegin,int pAEnd,string strB, int pBBegin,int pBEnd)
- {
- if (pABegin > pAEnd)
- {
- if (pBBegin > pBEnd)
- return 0;
- else
- return pBEnd - pBBegin + 1;
- }
- if (pBBegin > pBEnd)
- {
- if(pABegin > pAEnd)
- return 0;
- else
- return pAEnd - pABegin + 1;
- }
- if (strA[pABegin] == strB[pBBegin])
- {
- return calStringDis(strA,pABegin+1,pAEnd,strB,pBBegin+1,pBEnd);
- }
- else
- {
- int t1 = calStringDis(strA,pABegin+1,pAEnd,strB,pBBegin+2,pBEnd);
- int t2 = calStringDis(strA,pABegin+2,pAEnd,strB,pBBegin+1,pBEnd);
- int t3 = calStringDis(strA,pABegin+2,pAEnd,strB,pBBegin+2,pBEnd);
- return minValue(t1,t2,t3)+1;
- }
- }
以上解法来自《编程之美》,有什么地方须要改进的呢?问题在于:在递归的过程中,有些数据被反复计算了。
可使用动态规划方法解决
设
L(i,j)为使两个字符串和Ai和Bj相等的最小操作次数。
当ai==bj时 显然 L(i,j) = L(i-1,j-1)
当ai!=bj时 L(i,j) = min( L(i-1,j-1), L(i-1,j), L(i,j-1) ) + 1
int minValue(int a, int b, int c) { int t = a <= b ? a:b; return t <= c ? t:c; } int calculateStringDistance(string strA, string strB) { int lenA = (int)strA.length()+1; int lenB = (int)strB.length()+1; int **c = new int*[lenA]; for(int i = 0; i < lenA; i++) c[i] = new int[lenB]; for(int i = 0; i < lenA; i++) c[i][0] = i; for(int j = 0; j < lenB; j++) c[0][j] = j; c[0][0] = 0; for(int i = 1; i < lenA; i++) { for(int j = 1; j < lenB; j++) { if(strB[j-1] == strA[i-1]) c[i][j] = c[i-1][j-1]; else c[i][j] = minValue(c[i][j-1], c[i-1][j], c[i-1][j-1]) + 1; } } int ret = c[lenA-1][lenB-1]; for(int i = 0; i < lenA; i++) delete [] c[i]; delete []c; return ret; }