编程之美:第三章 结构之法 3.3计算字符串的相似度

来源:互联网 发布:2017年公务员网络培训 编辑:程序博客网 时间:2024/06/05 04:12
/*计算字符串的相似度:对于不同的字符串。我们定义一套操作方法来把两个不相同的字符串变相同,具体方法: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 abcdefabcd abmachao mayanmachao zhuwenping输出:0.500.330.250.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;}

0 0
原创粉丝点击