问题:程序中会使用大量的字符串,对不不同的字符串,定义一个方法来判断其相似的程度。我们定义了一套方法把两个不同的字符串变成相同,具体办法如下:
- 修改一个字符串(如把“a"改成“b")
- 增加一个字符(如把”abdd“变成”adcdd“)
- 删除一个字符(如把”abcdd“变成”abdd")
比如对于字符串”abcdefg“和”bcdefg“两个字符串来说,可以认为通过增加或者删除一个字符”a"的方式来达到目的。不管用哪种方式,都仅需1次操作。将此类操作的最少次数定义为2个字符串的距离,而相似度等于”距离+1“的倒数。即上面2个字符串的相似度为1/2=0.5
怎样实现这种算法呢?
分析与解法:
不难看出任何2个字符串之间的距离是有限的(都执行删除操作将字符串变成空字符串)
拿一个实例进行分析,从实例中找到将规模大的问题转变成规模小的同样的问题的方法(递归)。如果有字符串A=adexfs 和 B=aexfsed,它们第一个字母相同,就只用计算A[1,...,5]和B[1,...,6]的距离就可以了,假如第一个字符不相同,那么可以进行如下操作(endA,endB分别为A串、B串最后一个字符的索引)
- 删除A串的第一个字符,然后计算A[1,..endA]和B[0,...,endB]的距离
- 删除B串的第一个字符,然后计算A[0,...,endA]和B[1,...,endB]的距离
- 增加B串的第一个字符到A串第一个字符之前,然后计算A[0,...,endA]和B[1,...,endB]的距离
- 增加A串的第一个字符到B串第一个字符之前,然后计算A[1,...,endA]和B[0,...,endB]的距离
- 修改A串的第一个字符为B串的第一个字符,然后计算A[1,...,endA]和B[1,...,endB]的距离
- 修改B串的第一个字符为A串的第一个字符,然后计算A[1,...,endA]和B[1,...,endB]的距离
从上面分析看出,删除和增加是一个相对的操作,修改A或者修改B也是一个相对的操作,我们并不在乎2个字符串是变得相同之后结果是什么样的(或者换句话说不在乎具体操作,只在乎结果一样就行),所以我们可以将上面6个操作合并为:
- 一步操作之后(删除A或者增加B的第一个字符),再将A[1,..endA]和B[0,...,endB]变成相同字符串
- 一步操作之后(删除B或者增加A的第一个字符),再将A[0,...,endA]和B[1,...,endB]变成相同字符串
- 一步操作之后(修改A或者B的第一个字符),再将A[1,...,endA]和B[1,...,endB]变成相同的字符串
Memoization 方式来提高效率避免重复计算。
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
int minValue(int a,int b,int c)
{
int d=a;
if(b<d)d=b;
if(c<d)d=c;
return d;
}
int CalculateStringDistance(char* strA,int pABegin,int pAEnd,char * strB,int pBBegin,int pBEnd)
{
int tmp[pAEnd+2][pBEnd+2];
int i,j;
for(i=0;i<pAEnd+2;i++)
for(j=0;j<pBEnd+2;j++)
tmp[i][j]=-1;
StringDistance(strA,pABegin,pAEnd,strB,pBBegin,pBEnd,tmp);
}
int StringDistance(char* strA,int pABegin,int pAEnd,char* strB,int pBBegin,int pBEnd,int** tmp)
{
if(*((int*)tmp+(pBEnd+2)*pABegin+pBBegin)!=-1)return *((int*)tmp+(pBEnd+2)*pABegin+pBBegin);
if(pABegin>pAEnd)
{
if(pBBegin>pBEnd)
{
*((int*)tmp+(pBEnd+2)*pABegin+pBBegin)=0;
return *((int*)tmp+(pBEnd+2)*pABegin+pBBegin);
}
else
{
*((int*)tmp+(pBEnd+2)*pABegin+pBBegin)=pBEnd-pBBegin+1;
return *((int*)tmp+(pBEnd+2)*pABegin+pBBegin);
}
}
if(pBBegin>pBEnd)
{
if(pABegin>pAEnd)
{
*((int*)tmp+(pBEnd+2)*pABegin+pBBegin)=0;
return *((int*)tmp+(pBEnd+2)*pABegin+pBBegin);
}
else
{
*((int*)tmp+(pBEnd+2)*pABegin+pBBegin)=pAEnd-pABegin+1;
return *((int*)tmp+(pBEnd+2)*pABegin+pBBegin);
}
}
if(strA[pABegin]==strB[pBBegin])
{
StringDistance(strA,pABegin+1,pAEnd,strB,pBBegin+1,pBEnd,tmp);
}
else
{
int t1=StringDistance(strA,pABegin+1,pAEnd,strB,pBBegin,pBEnd,tmp);
int t2=StringDistance(strA,pABegin,pAEnd,strB,pBBegin+1,pBEnd,tmp);
int t3=StringDistance(strA,pABegin+1,pAEnd,strB,pBBegin+1,pBEnd,tmp);
*((int*)tmp+(pBEnd+2)*pABegin+pBBegin)=minValue(t1,t2,t3)+1;
return minValue(t1,t2,t3)+1;
}
}
int main(int argc,char* argv[])
{
char* strA="addaa";
char* strB="aiddllala";
printf("the result is %d",CalculateStringDistance(strA,0,4,strB,0,8));
}
编写代码时,会涉及到二维数组作为参数的问题,由于不能提前知道二维数组的第二维度,所以不能采用void function(int tmp[][10])这样的方式,只能通过指针的方式进行传参,void function(int** tmp) ,在函数里通过*((int*)tmp+i*n+j)的方式获取/设置tmp[i][j]的内容(其中n为二维数组第二维度)