/* 计算字符串的相似度: 对于不同的字符串。我们定义一套操作方法来把两个不相同的字符串变相同,具体方法: 1修改一个字符(如把'a'变成'b') 2增加一个字符(如把'abdd'变成'aebdd') 3删除一个字符(如把"travelling"变为"traveling") 比如,对于“abcdefg”和"abcdef"这两个字符串来说,我们认为可以通过增加/减少一个"g"的方式来达到目的。上面的两种方案都仅需要一次操作。把 这个操作需要的次数定义为两个字符串的距离,而相似度等于"距离+1"的倒数,也就是说"abcdefg" 和 "abcdef"的距离为1,相似度为1/2 = 0.5 给定任意两个字符串,你能否写出一个算法来计算他们的相似度呢? 分析: 两个字符串分距离肯定不超过它们的长度之和(可以通过删除操作把两个串都转化为空串)。考虑把这个问题转化为规模较小的问题。如果有两个串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]变成相同字符串 这个递归程序有些类似于前序和中序建立后序二叉树 优化:采用记忆化搜索,设定一个剪枝数组,如果有这个值,直接返回 输入: abcdefg abcdef abcd ab machao mayan machao zhuwenping 输出: 0.50 0.33 0.25 0.09 */ #include <stdio.h> #include <string.h> const int MAXSIZE = 10000; int min(int a,int b,int c) { int iMin = a < b ? a : b; return iMin < c ? iMin : c; } int calStrDistance(char* strA,char* strB,int iBegA,int iEndA,int iBegB,int iEndB,int g_iDisArr[][100]) { if(iBegA > iEndA)//如果起始位置大于终点位置,表明连最后一个字符也判断结束了,此时,计算另一个字符串中结束位置-开始位置+1,就是距离值 { if(iBegB > iEndB) { return g_iDisArr[iBegA][iBegB] = 0; } else { return g_iDisArr[iBegA][iBegB] = (iEndB - iBegB + 1); } } if(iBegB > iEndB) { if(iBegA > iEndA) { return g_iDisArr[iBegA][iBegB] = 0; } else { return g_iDisArr[iBegA][iBegB] = (iEndA - iBegA + 1); } } if(strA[iBegA] == strB[iBegB])//如果两个字符相同,那么双方都继续向下各自遍历 { if(g_iDisArr[iBegA+1][iBegB+1] != -1)//如果已经计算过了,那么直接返回 { return g_iDisArr[iBegA+1][iBegB+1]; } else { return g_iDisArr[iBegA+1][iBegB+1] = calStrDistance(strA,strB,iBegA+1,iEndA,iBegB+1,iEndB,g_iDisArr); } } else { int iDis1,iDis2,iDis3; if(g_iDisArr[iBegA+1][iBegB] != -1) { iDis1 = g_iDisArr[iBegA+1][iBegB]; } else { iDis1 = g_iDisArr[iBegA+1][iBegB] = calStrDistance(strA,strB,iBegA+1,iEndA,iBegB,iEndB,g_iDisArr);//如果不同,参见删除另一个字符串的当前字符,然后双方继续向下比较 } if(g_iDisArr[iBegA][iBegB+1] != -1) { iDis2 = g_iDisArr[iBegA][iBegB+1]; } { iDis2 = g_iDisArr[iBegA][iBegB+1] = calStrDistance(strA,strB,iBegA,iEndA,iBegB+1,iEndB,g_iDisArr);//如果不同,参加把一个字符串的当前字符加到另一个字符串的首位置,继续比较 } if(g_iDisArr[iBegA+1][iBegB+1] != -1) { iDis3 = g_iDisArr[iBegA+1][iBegB+1]; } else { iDis3 = g_iDisArr[iBegA+1][iBegB+1] = calStrDistance(strA,strB,iBegA+1,iEndA,iBegB+1,iEndB,g_iDisArr);//如果不同,参见修改一个字符串的第一个字符为另一个字符串的第一个字符 } return g_iDisArr[iBegA][iBegB] = ( min(iDis1,iDis2,iDis3) + 1 );//注意因为本身已经有一个值不同了,因此这里要加上1 } } void process() { char strA[MAXSIZE],strB[MAXSIZE]; while(EOF != scanf("%s %s",strA,strB)) { int g_iDisArr[100][100]; memset(g_iDisArr,-1,sizeof(g_iDisArr)); int iDis = calStrDistance(strA,strB,0,strlen(strA)-1,0,strlen(strB)-1,g_iDisArr); printf("%.2f\n",1.0/(iDis + 1));//相似度 = 距离加1的倒数 } } int main(int argc,char* argv[]) { process(); getchar(); return 0; }