您的位置:首页 > 其它

计算字符匹配相似度

2015-08-17 15:33 183 查看
编程之美有一道这样的题目,如下:

许多程序会大量使用字符串。对于不同的字符串,我们希望能够有办法判断其相似程序。我们定义一套操作方法来把两个不相同的字符串变得相同,具体的操作方法为:

1.修改一个字符(如把“a”替换为“b”);  

2.增加一个字符(如把“abdd”变为“aebdd”);

3.删除一个字符(如把“travelling”变为“traveling”);

比如,对于“abcdefg”和“abcdef”两个字符串来说,我们认为可以通过增加/减少一个“g”的方式来达到目的。上面的两种方案,都仅需要一 次 。把这个操作所需要的次数定义为两个字符串的距离,而相似度等于“距离+1”的倒数。也就是说,“abcdefg”和“abcdef”的距离为1,相似度 为1/2=0.5。

给定任意两个字符串,你是否能写出一个算法来计算它们的相似度呢?

根据编程之美的分析与解法,其是一种动态规划的应用,这题关键是其边界和推到公式。这道题编程之美分析六种情况,合并起来就只有三种,以下采纳编程之美的分析:

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[2,…,lenA]和B[2,…,lenB]变成相字符串。

3.一步操作之后,再将A[1,…,lenA]和B[2,…,lenB]变成相字符串。

假设A(a1a2a3 … am),B(b1b2b3 … bn),设要计算A 与B匹配的相似度的字符长度分别为i,j,L(i,j)为其两个字符长度分别为i和j的相似度,根据编程之美的分析那么就有四种情况:

当A 第一个字符和B第一个字符相等时,L(i,j)=L(i-1,j-1);

当A第一个字符和B第一个字符不想等时,采取上面1和6方式,L(i,j)=L(i-1,j)+1

当A第一个字符和B第一个字符不想等时,采取上面2和5方式,L(i,j)=L(i,j-1)+1

当A第一个字符和B第一个字符不想等时,采取上面3和4方式,L(i,j)fu=L(i-1,j-1)+1

边界:当A字符为空或B字符为空时,其相似度即为另一个字符串剩余的长度,即L(i,0)=i,L(0,j)=j.

还有一点就是在动态规划中,往往会遇到重复的计算,那怎么排除其重叠的自问题呢?可以用一个存储变量来标记每一个子问题是否被访问过,如果被问过直接直接读取取结果,减少计算时间,如果没被访问过则存储其第一次计算的结果。

这道题就用重叠的子问题出现,如编程之美的图:




图片来自:http://img.my.csdn.net/uploads/201212/09/1355029218_9181.jpg

好了,现在按照书本的代码,改进其代码,来排除重复计算子问题过程,代码如下:

#include <stdio.h>
#include<stdlib.h>
#include<string.h>
//返回最小值
int minValue(int a,int b,int c)
{
   if(a>b)
   {
      if(b>c)
         return c;
      else
         return b;
   }
   else
   {
      if(a>c)
         return c;
      else
         return a;
   }
}
int CalculateStringDistance(char *strA, int pABegin, int pAEnd, char *strB, int pBBegin, int pBEnd, int **subresult)
{
   //到达边界时,返回结果,减少计算
   if(pABegin>pAEnd)
      return pBBegin>pBEnd? 0:pBEnd-pBBegin+1;
   if(pBBegin>pBEnd)
      return pABegin>pAEnd? 0:pAEnd-pABegin+1;
      //符合第一种情况时
   if(strA[pABegin]==strB[pBBegin])
   {
      //若已计算过子问题,则直接返回,若没有则递归计算并保存其结果,这样就避免了重复计算重叠的子问题
      if(subresult[pABegin+1][pBBegin+1]!=0)
         return subresult[pABegin+1][pBBegin+1];
      else
      {
         subresult[pABegin+1][pBBegin+1]=CalculateStringDistance(strA,pABegin+1,pAEnd,strB,pBBegin+1,pBEnd,subresult);
         return subresult[pABegin+1][pBBegin+1];
      }
   }
   else
   {
      int a,b,c;
      a=b=c=0;
      //第三种情况时,若已计算过子问题,则直接返回,若没有则递归计算并保存其结果,这样就避免了重复计算重叠的子问题
      if(subresult[pABegin][pBBegin+1]!=0)
         a=subresult[pABegin][pBBegin+1];
      else
      {
         a=CalculateStringDistance(strA,pABegin,pAEnd,strB,pBBegin+1,pBEnd,subresult);
         subresult[pABegin][pBBegin+1]=a;
      }
      //第二种情况时,若已计算过子问题,则直接返回,若没有则递归计算并保存其结果,这样就避免了重复计算重叠的子问题
      if(subresult[pABegin+1][pBBegin]!=0)
         b=subresult[pABegin+1][pBBegin];
      else
         {
            b=CalculateStringDistance(strA,pABegin+1,pAEnd,strB,pBBegin,pBEnd,subresult);
            subresult[pABegin][pBBegin+1]=b;
         }
      //第四种情况时,若已计算过子问题,则直接返回,若没有则递归计算并保存其结果,这样就避免了重复计算重叠的子问题
      if(subresult[pABegin+1][pBBegin+1]!=0)
         c=subresult[pABegin+1][pBBegin+1];
      else
         {
            c=CalculateStringDistance(strA,pABegin+1,pAEnd,strB,pBBegin+1,pBEnd,subresult);
            subresult[pABegin+1][pBBegin+1]=c;
         }
      return minValue(a,b,c)+1;
   }
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: