计算字符匹配相似度

来源:互联网 发布:js 比大小 编辑:程序博客网 时间:2024/05/01 12:02

编程之美有一道这样的题目,如下:
许多程序会大量使用字符串。对于不同的字符串,我们希望能够有办法判断其相似程序。我们定义一套操作方法来把两个不相同的字符串变得相同,具体的操作方法为:
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;   }}
0 0
原创粉丝点击