找回密码
 立即注册→加入我们

QQ登录

只需一步,快速开始

搜索
热搜: 下载 VB C 实现 编写
查看: 3359|回复: 0

【算法】Levenshtein算法理解

[复制链接]

33

主题

1

回帖

561

积分

用户组: 大·技术宅

UID
580
精华
26
威望
28 点
宅币
341 个
贡献
0 次
宅之契约
0 份
在线时间
8 小时
注册时间
2014-12-8
发表于 2014-12-29 22:34:31 | 显示全部楼层 |阅读模式

欢迎访问技术宅的结界,请注册或者登录吧。

您需要 登录 才可以下载或查看,没有账号?立即注册→加入我们

×
算法介绍:
Levenshtein算法是计算两个字符串之间的最小编辑距离的算法,所谓的最小编辑距离就是把字符串A通过添加,删除,替换字符的方式转变成B所需要的最少步骤。俄罗斯科学家Vladimir Levenshtein在1965年提出这个概念,所以叫做Levenshtein算法。
算法的流程:
1:计算strA的长度n,strB的长度m
2:如果n=0,则最小编辑距离是m,m=0,则最小编辑距离是n
3:构造一个 (m+1)*(n+1)的矩阵Arr,并初始化矩阵的第一行和第一列分别为0-n,0-m
4:两重循环,遍历strA,在此基础上遍历strB,如果strA[i ]=strB[j],那么cost=0,否则cost=1,判断Arr[j-1][i ]+1,Arr[j][i-1]+1,Arr[j-1][i-1]+cost的最小值,将最小值赋值给Arr[j][i ]。
5:循环结束后,矩阵的最后一个元素就是最小编辑距离。
根据以上算法流程的C++代码:
  1. #include <string>
  2. #include <iostream>
  3. using namespace std;

  4. int levenshtein(string str1,string str2);

  5. void main()
  6. {
  7.         string str1,str2;
  8.         cout<<"please input a string str1:"<<endl;
  9.         cin>>str1;
  10.         cout<<"please input a string str2:"<<endl;
  11.         cin>>str2;
  12.         cout<<"change from str1 to str2 needs: "<<levenshtein(str1,str2)<<endl;
  13. }

  14. int levenshtein(string str1,string str2)
  15. {
  16.         int n = str1.size();
  17.         int m = str2.size();
  18.         if ( n == 0)
  19.                 return m;
  20.         if ( m == 0)
  21.                 return n;
  22.         int **Arr = (int**)malloc( (m+1)*sizeof(int*) );
  23.         for (int i=0;i<=m;i++)
  24.                 Arr[i ] = (int*)malloc( (n+1)*sizeof(int) );
  25.         int cost = 0;
  26.         for(int i=0;i<=n;i++)
  27.                 Arr[0][i ] = i;
  28.         for(int j=0;j<=m;j++)
  29.                 Arr[j][0] = j;
  30.         for(int i=1;i<=n;i++)
  31.                 for(int j=1;j<=m;j++)
  32.                 {
  33.                         if (str1[i-1] == str2[j-1])
  34.                                 cost = 0;
  35.                         else
  36.                                 cost = 1;
  37.                         Arr[j][i ] = Arr[j-1][i ]+1<Arr[j][i-1]+1?Arr[j-1][i ]+1:Arr[j][i-1] +1 ;
  38.                         Arr[j][i ] = Arr[j][i ]<Arr[j-1][i-1]+cost?Arr[j][i ]:Arr[j-1][i-1]+cost;
  39.                 }
  40.         int nEdit = Arr[m][n];

  41.         for(int i=0;i<=m;i++)
  42.                 free(Arr[i ]);
  43.         free(Arr);
  44.        
  45.         return nEdit;
  46. }
复制代码
算法原理:
1:为什么要把矩阵首行以及首列初始化为0-n,0-m
我们先说说列初始化0-m表示的原因,它意思是strA从0个元素转变成strB的前i个元素的操作步骤数,这当然就很容易理解了,从一个空白的字符串转换为包含一个字符的前i个的字符的子字符串,当然需要添加i个字符了。同理,可以解释第一行初始化为0-n的原因。
2:为什么选择Arr[j-1][i ]+1,Arr[j][i-1]+1,Arr[j-1][i-1]+cost的最小值赋给Arr[j][i ]
通过1,我们应该理解这里的Arr[j][i ]表示的意思,它的意思就是strA的前i个字符转变成strB的前j个字符的最小编辑距离。
Arr[j-1][i ]表示的是strA的前i个字符转变成strB的前j-1个字符的操作步骤,那么把strA的前i个字符转变成strB的前j个字符,只需要在strA的前i个字符后面加上t[j],所以最终操作数是Arr[j-1][i ]+1;
Arr[j][i-1]表示的是strA的前i-1个字符转变成strB的前j个字符的操作步骤,那么把strA的前i个字符转变成strB的前j个字符,只需要在strA的第i个字符删除即可,所以最终操作数是Arr[j][i-1]+1;
Arr[j-1][i-1]表示的strA的前i-1个字符转变成strB的前j-1个字符的操作步骤,那么把strA的前i个字符变成strB的前j个字符,只需要看strA的第i个字符和strB的第j个字符是否相等,相等则最终操作数是Arr[j-1][i-1],不相等则最终操作数是Arr[j-1][i-1]+1;
以上三种根据前者转换的方法得到的结果都可能是最少的编辑距离,所以我们选择三者的最小值作为Arr[j][i ]的值。
算法实例:
strA = "acdf",strB = "abc"
     a c  d  f
  0  1 2  3  4
a 1
b 2
c 3
当i=0时,strA的前0个字符串str = null,
j = 0,str->null ,k =0,即Arr[0][0] =0;
j =1,str->a,k =1,即Arr[0][1] =1;
j =2,str->ab,k=2,即Arr[0][2]=2;
j =3,str->abc,k=3,即Arr[0][3] =3;
当 i =1时,strA的前1个字符串str = a,
j =1,str->a:strA[0,i-1]--> strB[0,j] =1(null-->a), strA[0,i]-->strB[0,j-1]=1(a->null), strA[0,i-1]-->strB[0,j-1]=0(null-->null),  从而证明了Arr[1][1] =0;
j =2,str->ab,....(同理)
j =3,str->abc,...(同理)
i=2,3,4(同理)


算法改进:
从算法的计算中我们可以看到,虽然建立了一个(m+1)×(n+1)的矩阵,但是我们每次只用到了矩阵中的两列或者两行,所以从这点来改进算法,提高算法的空间利用率。
  1. int Levenshtein(string str1,string str2)
  2. {
  3.         int n = str1.size();
  4.         int m = str2.size();
  5.         if ( n == 0)
  6.                 return m;
  7.         if ( m == 0)
  8.                 return n;
  9.         vector<int> vec1(m+1);
  10.         vector<int> vec2(m+1);
  11.         for(int i=0;i<=m;i++)
  12.                 vec1[i ] = i;
  13.         int cost = 0;

  14.         for(int i=1;i<=n;i++)
  15.         {
  16.                 vec2[0] = i;
  17.                 for(int j=1;j<=m;j++)
  18.                 {
  19.                         if( str1[i-1] == str2[j-1] )
  20.                                 cost = 0;
  21.                         else
  22.                                 cost = 1;
  23.                         vec2[j] = vec2[j-1]+1 < vec1[j]+1 ? vec2[j-1]+1 : vec1[j]+1;
  24.                         vec2[j] = vec2[j] < vec1[j-1]+cost ? vec2[j] : vec1[j-1]+cost;
  25.                 }
  26.                 vec1 = vec2;
  27.         }
  28.         return vec2.back();
  29. }
复制代码
回复

使用道具 举报

QQ|Archiver|小黑屋|技术宅的结界 ( 滇ICP备16008837号 )|网站地图

GMT+8, 2024-4-25 17:21 , Processed in 0.044134 second(s), 31 queries , Gzip On.

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表