Levenshtein

Levenshtein算法,用于计算两个字符串之间的Levenshtein距离。而Levenshtein距离又称为编辑距离,是指两个字符串之间,由一个转换成另一个所需的最少编辑操作次数。许可的编辑操作包括将一个字符替换成另一个字符,插入一个字符,删除一个字符。

概述

Levenshtein距离用来描述两个字符串之间的差异。我在一个网络爬虫程序里面使用这个算法来比较两个网页之间的版本,如果网页的内容有足够多的变动,我便将它更新到我的数据库。

说明

原来的算法是创建一个大小为StrLen1*StrLen2的矩阵。如果所有字符串加起来是1000个字符那么长的话,那么这个矩阵就会是1M;如果字符串是10000个字符,那么矩阵就是100M。如果元素都是整数(这里是指数字,Int32)的话,那么矩阵就会是4*100M == 400MB这么大,唉……

现在的算法版本只使用2*StrLen个元素,这使得后面给出的例子成为2*10,000*4 = 80 KB。其结果是,不但内存占用更少,而且速度也变快了!因为这使得内存分配只需要很少的时间来完成。当两个字符串的长度都是1k左右时,新算法的效率是旧算法的两倍!

示例

原来的版本将会创建一个矩阵[6+1, 5+1],而我的新算法将会创建两个向量[6+1](黄色元素)。在这两个算法版本中,字符串的顺序是无关紧要、无所谓的,也就是说,它也可以是矩阵[5+1, 6+1]和两个向量[5+1]。

新的算法

步骤

Step 		Description

1 	Set n to be the length of s.
	Set m to be the length of t.
	If n = 0, return m and exit.
	If m = 0, return n and exit.
	Construct a matrix containing 0...n rows and 0...m columns.

2 	Initialize the first row    to 0...n.
	Initialize the first column to 0...m.

3 	Examine each character of s (i from 1 to n).
4 	Examine each character of t (j from 1 to m).
5 	If s[i] == t[j], the cost = 0.
	If s[i] != t[j], the cost = 1.


6 	Set cell d[i,j] of the matrix equal to the minimum of:
	a. The cell immediately above plus 1:                       d[i-1, j]   + 1.
	b. The cell immediately to the left plus 1:                 d[i, j-1]   + 1.
	c. The cell diagonally above and to the left plus the cost: d[i-1, j-1] + cost.

7 	After the iteration steps (3, 4, 5, 6) are complete, the distance is found in cell d[n, m]. 
#include <stdio.h>

int minimum (int a, int b, int c)
{
    int min = a;
    if (b < min)
        min = b;
    if (c < min)
        min = c;
    return min;
}

int LevenshteinDis(char* s, char* t)
{
    int  i, j, cost;
    int  n = strlen(s), m = strlen(t);
    char s_i, t_j;
    
    // Step 1
    if (n == 0)
        return m;
    if (m == 0)
        return n;
    int d[n+1][m+1];
    
    // Step 2: n for row, m for col
    for (i = 0; i <= n; ++i)
        d[i][0] = i;
    for (j = 0; j <= m; ++j)
        d[0][j] = j;
        
    // Step 3
    for (i = 1; i <= n; ++i)
    {
        s_i = s[i-1]; // above
        // Step 4
        for (j = 1; j <= m; ++j)
        {
            t_j = t[j-1]; // left
            // Step 5
            if (s_i == t_j)
                cost = 0;
            else
                cost = 1;
            // Step 6
            d[i][j] = minimum(d[i-1][j]+1, d[i][j-1]+1, d[i-1][j-1]+cost);
        }    
    }
    // Step 7
    return d[n][m];      
}    

int main()
{
    char *s = "levenshtein";
    char *t = "meilenstein";
    
    printf("%d\n", LevenshteinDis(s, t));
    
    return 0;
}



步骤说明
1设置n为字符串s的长度。("GUMBO")
设置m为字符串t的长度。("GAMBOL")
如果n等于0,返回m并退出。
如果m等于0,返回n并退出。
构造两个向量v0[m+1] 和v1[m+1],串联0..m之间所有的元素。
2初始化 v0 to 0..m。
3检查 s (i from 1 to n) 中的每个字符。
4检查 t (j from 1 to m) 中的每个字符
5如果 s[i] 等于 t[j],则编辑代价为 0;
如果 s[i] 不等于 t[j],则编辑代价为1。
6设置单元v1[j]为下面的最小值之一:
a、紧邻该单元上方+1:v1[j-1] + 1
b、紧邻该单元左侧+1:v0[j] + 1
c、该单元对角线上方和左侧+cost:v0[j-1] + cost
7在完成迭代 (3, 4, 5, 6) 之后,v1[m]便是编辑距离的值。

本小节将演示如何计算"GUMBO"和"GAMBOL"两个字符串的Levenshtein距离。

步骤1、2
 v0v1    
  GUMBO
 012345
G1     
A2     
M3     
B4     
O5     
L6     
步骤3-6,当 i = 1

 

 v0v1    
  GUMBO
 012345
G10    
A21    
M32    
B43    
O54    
L65    
步骤3-6,当 i = 2
  v0v1   
  GUMBO
 012345
G101   
A211   
M322   
B433   
O544   
L655   
步骤3-6,当 i = 3

 

   v0v1  
  GUMBO
 012345
G1012  
A2112  
M3221  
B4332  
O5443  
L6554  
步骤3-6,当 i = 4

 

    v0v1 
  GUMBO
 012345
G10123 
A21123 
M32212 
B43321 
O54432 
L65543 
步骤3-6,当 i = 5

 

     v0v1
  GUMBO
 012345
G101234
A211234
M322123
B433212
O544321
L655432
步骤7

编辑距离就是矩阵右下角的值,v1[m] == 2。由"GUMBO"变换为"GAMBOL"的过程对于我来说是很只管的,即通过将"A"替换为"U",并在末尾追加"L"这样子(实际上替换的过程是由移除和插入两个操作组合而成的)。

改良

如果您确信你的字符串永远不会超过2^16(65536)个字符,那么你可以使用ushort来表示而不是int,如果字符串少于2^8个,还可以使用byte。我觉得这个算法用非托管代码实现的话可能会更快,但我没有试过。

参考文献

  • Levenshtein Distance, in Three Flavors
  • http://www.cnblogs.com/ymind/archive/2012/03/27/fast-memory-efficient-Levenshtein-algorithm.html
  • http://people.cs.pitt.edu/~kirk/cs1501/Pruhs/Spring2006/assignments/editdistance/Levenshtein%20Distance.htm
  • http://www.csse.monash.edu.au/~lloyd/tildeAlgDS/Dynamic/Edit/
  • http://www.codeproject.com/Articles/13525/Fast-memory-efficient-Levenshtein-algorithm
  • http://www.levenshtein.net/index.html

下载代码请前往原文:http://www.codeproject.com/Articles/13525/Fast-memory-efficient-Levenshtein-algorithm

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值