字符串相似度三种算法介绍

余弦相似度

计算公式为:

  P(A,B) = sqrt(A × B) / (|A| × |B|)

设有两个字符串:

  1. ABCDEFG
  2. ABCHIJK

其中共有11个字符,为:

  A B C D E F G H I J K

如果,不考虑他们之间的关联性以及顺序等隐私,那么可以讲这两个字符串转换成两个11维空间中的向量:

  1. {1、1、1、1、1、1、1、0、0、0、0}
  2. {1、1、1、0、0、0、0、1、1、1、1}

那,计算他们之间的相似度为:

  P = sqrt(3) / (sqrt(7) × sqrt(7)) = 0.2474358297

矩阵相似度

给定两个长度相等的字符串,在移动的过程中比较:

a b c d d a c b c b    
    a a d a c c b d d c

首先有几个变量:

  1. n:字符串的长度,此时为10;
  2. m:相同的字符,此时为3,包括d、a、c;
  3. r:两个字符串重叠部分,此时为8;

那么给出定义:

  1. 重叠率:L = r / n。
  2. 匹配率:M = m / n。
  3. 相似度:Q = M^2 × L = (m^2 / n^2) × (r / n)。

其实为什么这样定义也很好理解,将Q变形一下就可以得到:

  Q = (m^2 / r^2) × (r / n)

前半部分表示了当前相同的比率,后半部分表示了重叠的比率,然后呢,废话就不多说了。其实,还有一个要考虑的地方,举个例子:

  str1:abcabc

  str2:abcdabc

str1str2的相似度是很高的,但是,在移位错开的过程中根本没办法找到这种匹配。想想其实原因也是非常简单的:把所有的字符都死板地粘合在了一起!那么,我们要做的其实就是将他们打散来匹配。首先,根据字符串A和字符串B来构造矩阵R

  Ai和Bi+j相同时,Rij = 1;否则,Rij = 0。

那么,现在要做的事情就是,在矩阵R中寻找一条路径,使得这条路径上的1最多,这个问题和求两个字符串的最大匹配很像的DP问题,这里就不啰嗦了。

字符串编辑距离

还有一种衡量两个字符串之间的差异性的方法是,计算两个字符串转换时候需要的最少操作,需要的操作越少说明这两个字符串越相似。

假设字符串的操作只有三种:

  1. 插入一个字符;
  2. 删除一个字符;
  3. 替换一个字符;

两个字符串之间的编辑距离定义为:从字符串str1str2的最少的操作次数。首先,编辑距离是不会大于str1.length + str2.length的。假设求字符AB的编辑距离,考虑下面几种情况:

如果A[i] = B[j],那么这时候还需要操作吗?

这个时候的删除和替换操作只会让情况变得更坏,而且插入操作不会使情况变得更好,所以此时F(i, j) = F(i-1, j-1)

如果A[i] != B[j],怎么办呢?

a、从F(i-1, j-1)变过来,这时候只需要把A[i]替换为B[j]即可;

b、从F(i-1, j)变过来,这时候只需要将A[i]删除即可;

c、从F(i, j-1)变过来,这时候只需要在A[i]后插入字符B[j]即可;

那么此时,F(i, j) = min{F(i-1,j-1),F(i-1,j),F(i,j-1)} + 1

注:其中F(i, j)表示A[0..i]和B[0..j]之间的编辑距离。

附录:Levenshtein Distance (编辑距离) 算法详解

编辑距离即从一个字符串变换到另一个字符串所需要的最少变化操作步骤(以字符为单位,如son到sun,s不用变,将o->s,n不用变,故操作步骤为1)。

为了得到编辑距离,我们画一张二维表来理解,以beauty和batyu为例:

图示如1单元格位置即是两个单词的第一个字符[b]比较得到的值,其值由它上方的值(1)、它左方的值(1)和、它左上角的值(0)来决定。当单元格所在的行和列所对应的字符(如3对应的是a和b)相等时,它左上角的值+0,否则加1(如在1处,[b]=[b]故左上角的值加0即0+0=0,而在2处[b]!=[b] 故左上角的值加1即1+1=2)。然后再将单元格的左单元格和上单元格的值分别加1,(,然后取相加后得到的三个结果的最小值作为该单元的值如1处相加后其左上、左、上的值为(0,2,2),故1单元格的值为0,而在3处,得到的值为(2,3,1),故3单元格的值为1)。

算法证明

这个算法计算的是将s[1…i]转换为t[1…j](例如将beauty转换为batyu)所需最少的操作数(也就是所谓的编辑距离),这个操作数被保存在d[i,j](d代表的就是上图所示的二维数组)中。

  • 在第一行与第一列肯定是正确的,这也很好理解,例如我们将beauty转换为空字符串,我们需要进行的操作数为beauty的长度(所进行的操作为将beauty所有的字符丢弃)。
  • 我们对字符可能进行的操作有三种:
  • 将s[1…n]转换为t[1…m]当然需要将所有的s转换为所有的t,所以,d[n,m](表格的右下角)就是我们所需的结果。
  • 如果我们可以使用k个操作数把s[1…i]转换为t[1…j-1],我们只需要把t[j]加在最后面就能将s[1…i]转换为t[1…j],操作数为k+1
  • 如果我们可以使用k个操作数把s[1…i-1]转换为t[1…j],我们只需要把s[i]从最后删除就可以完成转换,操作数为k+1
  • 如果我们可以使用k个操作数把s[1…i-1]转换为t[1…j-1],我们只需要在需要的情况下(s[i] != t[j])把s[i]替换为t[j],所需的操作数为k+cost(cost代表是否需要转换,如果s[i]==t[j],则cost为0,否则为1)。

这个证明过程只能证明我们可以得到结果,但并没有证明结果是最小的(即我们得到的是最少的转换步骤)。所以我们引进了另外一个算法,即d[i,j]保存的是上述三种操作中操作数最小的一种。这就保证了我们获得的结果是最小的操作数

可能进行的改进

    • 现在的算法复杂度为O(mn),可以将其改进为O(m)。因为这个算法只需要上一行和当前行被存储下来就可以了。
    • 如果需要重现转换步骤,我们可以把每一步的位置和所进行的操作保存下来,进行重现。
    • 如果我们只需要比较转换步骤是否小于一个特定常数k,那么只计算高宽宽为2k+1的矩形就可以了,这样的话,算法复杂度可简化为O(kl),l代表参加对比的最短string的长度。
    • 我们可以对三种操作(添加,删除,替换)给予不同的权值(当前算法均假设为1,我们可以设添加为1,删除为0,替换为2之类的),来细化我们的对比。
    • 如果我们将第一行的所有cell初始化为0,则此算法可以用作模糊字符查询。我们可以得到最匹配此字符串的字符串的最后一个字符的位置(index number),如果我们需要此字符串的起始位置,我们则需要存储各个操作的步骤,然后通过算法计算出字符串的起始位置。
    • 这个算法不支持并行计算,在处理超大字符串的时候会无法利用到并行计算的好处。但我们也可以并行的计算cost values(两个相同位置的字符是否相等),然后通过此算法来进行整体计算。
    • 如果只检查对角线而不是检查整行,并且使用延迟验证(lazy evaluation),此算法的时间复杂度可优化为O(m(1+d))(d代表结果)。这在两个字符串非常相似的情况下可以使对比速度速度大为增加。
时间: 2024-12-18 02:11:00

字符串相似度三种算法介绍的相关文章

最近公共祖先(三种算法)

最近研究了一下最近公共祖先算法,根据效率和实现方式不同可以分为基本算法.在线算法和离线算法.下面将结合hihocoder上的题目分别讲解这三种算法. 1.基本算法 对于最近公共祖先问题,最容易想到的算法就是从根开始遍历到两个查询的节点,然后记录下这两条路径,两条路径中距离根节点最远的节点就是所要求的公共祖先. 题目参见 #1062 : 最近公共祖先·一 附上AC代码,由于记录的方式采取的是儿子对应父亲,所以实现的时候有点小技巧,就是对第一个节点的路径进行标记,查找第二个节点的路径时一旦发现访问到

字符串匹配的三种算法

下面将介绍三种有关字符串匹配的算法,一种是朴素的匹配算法,时间复杂度为O(mn),也就是暴力求解.这种方法比较简单,容易实现.一种是KMP算法,时间复杂度为O(m+n),该算法的主要任务是求模式串的next数组.另外还有一种对KMP算法的改进,主要是求nextval数组. 第一种朴素的匹配算法: int index(char str[], char subStr[]) { int i = 0, j = 0,index = 0; while (str[i] != '\0' && subStr

Mysql Binlog三种格式介绍及分析【转】

一.Mysql Binlog格式介绍       Mysql binlog日志有三种格式,分别为Statement,MiXED,以及ROW! 1.Statement:每一条会修改数据的sql都会记录在binlog中. 优点:不需要记录每一行的变化,减少了binlog日志量,节约了IO,提高性能.(相比row能节约多少性能 与日志量,这个取决于应用的SQL情况,正常同一条记录修改或者插入row格式所产生的日志量还小于Statement产生的日志量,但是考虑到如果带条 件的update操作,以及整表

Opencv——彩色图像灰度化的三种算法

为了加快处理速度在图像处理算法中,往往需要把彩色图像转换为灰度图像.24为彩色图像每个像素用3个字节表示,每个字节对应着RGB分量的亮度. 当RGB分量值不同时,表现为彩色图像:当RGB分量相同时,变现为灰度图像: 一般来说,转换公式有3中. (1)Gray(i,j)=[R(i,j)+G(i,j)+B(i,j)]/3; (2)Gray(i,j)=0.299*R(i,j)+0.587*G(i,j)+0.144*B(i,j); (3)Gray(i,j)=G(i,j);//从2可以看出G的分量比较大所

Java利用 DES / 3DES / AES 这三种算法分别实现 对称加密

转载请注明出处:http://blog.csdn.net/smartbetter/article/details/54017759 有两句话是这么说的: 1)算法和数据结构就是编程的一个重要部分,你若失掉了算法和数据结构,你就把一切都失掉了. 2)编程就是算法和数据结构,算法和数据结构是编程的灵魂. 注意,这可不是我说的,是无数程序员总结的,话说的很实在也很精辟,若想长久可持续发展,多研究算法还是很有必要的,今天我给大家说说加密算法中的对称加密算法,并且这里将教会大家对称加密算法的编程使用.包含

快速排序、归并排序、堆排序三种算法性能比较

快速排序.归并排序.堆排序三种排序算法的性能谁最好呢?网上查了一下说快速排序最快.其次是归并排序,最差的是堆排序:而理论上三种排序算法的时间复杂度都是O(nlogn),只不过快速排序最差的会达到O(n^2),但是数据的随机性会消除这一影响,今天就来实际比较一下: 1 #include <iostream> 2 #include<time.h> 3 using namespace std; 4 #define MAX 100000000 5 int data1[MAX],data2[

Java常用三种算法排序比较

Java常用三种算法排序比较 冒泡排序: package demo1; /** * * @author xiaoye 2014-5-13 */ /** * 有N 个数据需要排序,则从第0 个数开始,依次比较第0 和第1 个数据, * 如果第0 个大于第1 个则两者交换,否则什么动作都不做,继续比较第 1 个第2个-, * 这样依次类推,直至所有数据都"冒泡"到数据顶上. 冒泡排序的效率 O(N*N ),比较 N*N/2 ,交换N*N/4 . */ public class Bubble

三种MPM介绍

三种MPM介绍                                                                               Apache 2.X  支持插入式并行处理模块,称为多路处理模块(MPM).在编译apache时必须选择也只能选择一个MPM,对类UNIX系统,有几个不同的MPM可供选择,它们会影响到apache的速度和可伸缩性. Prefork MPM : 这个多路处理模块(MPM)实现了一个非线程型的.预派生的web服务器,它的工作方式类

缓存算法(FIFO 、LRU、LFU三种算法的区别)

缓存算法(FIFO .LRU.LFU三种算法的区别) FIFO算法# FIFO 算法是一种比较容易实现的算法.它的思想是先进先出(FIFO,队列),这是最简单.最公平的一种思想,即如果一个数据是最先进入的,那么可以认为在将来它被访问的可能性很小.空间满的时候,最先进入的数据会被最早置换(淘汰)掉. FIFO 算法的描述:设计一种缓存结构,该结构在构造时确定大小,假设大小为 K,并有两个功能: set(key,value):将记录(key,value)插入该结构.当缓存满时,将最先进入缓存的数据置