计算字符匹配相似度
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
好了,现在按照书本的代码,改进其代码,来排除重复计算子问题过程,代码如下:
许多程序会大量使用字符串。对于不同的字符串,我们希望能够有办法判断其相似程序。我们定义一套操作方法来把两个不相同的字符串变得相同,具体的操作方法为:
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; } }
相关文章推荐
- fitsSystemWindow作用
- hdu 1166 敌兵布阵(树状数组)
- 如何设置div的宽度为100%-xx px?
- 简单的Java Socket示例
- Codeforces Round #316 (Div. 2) E. Pig and Palindromes (DP)
- C语言的冒泡排序和快速排序算法使用实例
- freemarker根据模板生成word文档,换行
- 消除jquery Mobile自动添加的样式
- Android绘制波浪曲线,效果很赞的。
- 定位锚平滑滚动jQuery代码
- 微软TechNet关于TLS的细节的描述
- 为什么程序员的业余项目大多都死了?
- 我为什么维护这个关于技术的博客
- iptables
- FrameLayout布局
- GXDialog 使用方法
- 拖尾飘带的优化实践
- vs 2012打开vs2013的sln
- 人心的距离
- 常见26个jquery使用技巧详解(比如禁止右键点击、隐藏文本框文字等)