计算字符串相似度

1 篇文章 0 订阅

问题:程序中会使用大量的字符串,对不不同的字符串,定义一个方法来判断其相似的程度。我们定义了一套方法把两个不同的字符串变成相同,具体办法如下:

  1. 修改一个字符串(如把“a"改成“b")
  2. 增加一个字符(如把”abdd“变成”adcdd“)
  3. 删除一个字符(如把”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串最后一个字符的索引)

  1. 删除A串的第一个字符,然后计算A[1,..endA]和B[0,...,endB]的距离
  2. 删除B串的第一个字符,然后计算A[0,...,endA]和B[1,...,endB]的距离
  3. 增加B串的第一个字符到A串第一个字符之前,然后计算A[0,...,endA]和B[1,...,endB]的距离
  4. 增加A串的第一个字符到B串第一个字符之前,然后计算A[1,...,endA]和B[0,...,endB]的距离
  5. 修改A串的第一个字符为B串的第一个字符,然后计算A[1,...,endA]和B[1,...,endB]的距离
  6. 修改B串的第一个字符为A串的第一个字符,然后计算A[1,...,endA]和B[1,...,endB]的距离
从上面分析看出,删除和增加是一个相对的操作,修改A或者修改B也是一个相对的操作,我们并不在乎2个字符串是变得相同之后结果是什么样的(或者换句话说不在乎具体操作,只在乎结果一样就行),所以我们可以将上面6个操作合并为:

  1. 一步操作之后(删除A或者增加B的第一个字符),再将A[1,..endA]和B[0,...,endB]变成相同字符串
  2. 一步操作之后(删除B或者增加A的第一个字符),再将A[0,...,endA]和B[1,...,endB]变成相同字符串
  3. 一步操作之后(修改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为二维数组第二维度)

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值