Damerau-Levenshtein距离实施

时间:2014-03-10 18:12:05

标签: javascript algorithm ocr levenshtein-distance

我正在尝试在JS中创建一个damerau-levenshtein距离函数。

我在WIkipedia上找到了关于算法的描述,但它们并没有实现。它说:

  

设计合适的算法来计算不受限制的   Damerau-Levenshtein距离注意到始终存在最优   编辑操作的序列,其中一次转置的字母永远不会   事后修改。因此,我们只需考虑两种对称方式   多次修改子串:(1)转置字母和   在它们之间插入任意数量的字符,或(2)删除a   字符序列和转置相邻的字母   删除后。这个想法的直接实现给出了   立方复杂度算法:O \ left(M \ cdot N \ cdot \ max(M,N)   \ right),其中M和N是字符串长度。运用的思想   Lowrance和Wagner,[7]这种天真算法可以改进   在最坏的情况下,O \ left(M \ cdot N \ right)。有趣的是   可以修改bitap算法来处理转置。见   [1]的信息检索部分,用于这种情况的一个例子   适应。

     

https://en.wikipedia.org/wiki/Damerau%E2%80%93Levenshtein_distance

第[1]节指向http://acl.ldc.upenn.edu/P/P00/P00-1037.pdf,这对我来说更复杂。

如果我理解正确,那么创建一个实现并不容易。

这是我目前使用的levenshtein实现:

levenshtein=function (s1, s2) {
  //       discuss at: http://phpjs.org/functions/levenshtein/
  //      original by: Carlos R. L. Rodrigues (http://www.jsfromhell.com)
  //      bugfixed by: Onno Marsman
  //       revised by: Andrea Giammarchi (http://webreflection.blogspot.com)
  // reimplemented by: Brett Zamir (http://brett-zamir.me)
  // reimplemented by: Alexander M Beedie
  //        example 1: levenshtein('Kevin van Zonneveld', 'Kevin van Sommeveld');
  //        returns 1: 3

  if (s1 == s2) {
    return 0;
  }

  var s1_len = s1.length;
  var s2_len = s2.length;
  if (s1_len === 0) {
    return s2_len;
  }
  if (s2_len === 0) {
    return s1_len;
  }

  // BEGIN STATIC
  var split = false;
  try {
    split = !('0')[0];
  } catch (e) {
    // Earlier IE may not support access by string index
    split = true;
  }
  // END STATIC
  if (split) {
    s1 = s1.split('');
    s2 = s2.split('');
  }

  var v0 = new Array(s1_len + 1);
  var v1 = new Array(s1_len + 1);

  var s1_idx = 0,
    s2_idx = 0,
    cost = 0;
  for (s1_idx = 0; s1_idx < s1_len + 1; s1_idx++) {
    v0[s1_idx] = s1_idx;
  }
  var char_s1 = '',
    char_s2 = '';
  for (s2_idx = 1; s2_idx <= s2_len; s2_idx++) {
    v1[0] = s2_idx;
    char_s2 = s2[s2_idx - 1];

    for (s1_idx = 0; s1_idx < s1_len; s1_idx++) {
      char_s1 = s1[s1_idx];
      cost = (char_s1 == char_s2) ? 0 : 1;
      var m_min = v0[s1_idx + 1] + 1;
      var b = v1[s1_idx] + 1;
      var c = v0[s1_idx] + cost;
      if (b < m_min) {
        m_min = b;
      }
      if (c < m_min) {
        m_min = c;
      }
      v1[s1_idx + 1] = m_min;
    }
    var v_tmp = v0;
    v0 = v1;
    v1 = v_tmp;
  }
  return v0[s1_len];
} 

您对构建此类算法的想法是什么?如果您认为它太复杂,我可以做些什么来使'l'(L小写)和'I'(i大写)之间没有区别。

1 个答案:

答案 0 :(得分:3)

要点@doukremt:https://gist.github.com/doukremt/9473228

在Javascript中提供以下内容。

您可以更改加权对象中的操作权重。

var levenshteinWeighted= function(seq1,seq2)
{
    var len1=seq1.length;
    var len2=seq2.length;
    var i, j;
    var dist;
    var ic, dc, rc;
    var last, old, column;

    var weighter={
        insert:function(c) { return 1.; },
        delete:function(c) { return 0.5; },
        replace:function(c, d) { return 0.3; }
    };

    /* don't swap the sequences, or this is gonna be painful */
    if (len1 == 0 || len2 == 0) {
        dist = 0;
        while (len1)
            dist += weighter.delete(seq1[--len1]);
        while (len2)
            dist += weighter.insert(seq2[--len2]);
        return dist;
    }

    column = []; // malloc((len2 + 1) * sizeof(double));
    //if (!column) return -1;

    column[0] = 0;
    for (j = 1; j <= len2; ++j)
        column[j] = column[j - 1] + weighter.insert(seq2[j - 1]);

    for (i = 1; i <= len1; ++i) {
        last = column[0]; /* m[i-1][0] */
        column[0] += weighter.delete(seq1[i - 1]); /* m[i][0] */
        for (j = 1; j <= len2; ++j) {
            old = column[j];
            if (seq1[i - 1] == seq2[j - 1]) {
                column[j] = last; /* m[i-1][j-1] */
            } else {
                ic = column[j - 1] + weighter.insert(seq2[j - 1]);      /* m[i][j-1] */
                dc = column[j] + weighter.delete(seq1[i - 1]);          /* m[i-1][j] */
                rc = last + weighter.replace(seq1[i - 1], seq2[j - 1]); /* m[i-1][j-1] */
                column[j] = ic < dc ? ic : (dc < rc ? dc : rc);
            }
            last = old;
        }
    }

    dist = column[len2];
    return dist;
}