【问题标题】:Asymmetric Levenshtein distance不对称 Levenshtein 距离
【发布时间】:2015-01-28 06:25:04
【问题描述】:

给定两个位字符串 x 和 y,其中 x 比 y 长,我想计算它们之间的 Levensthein 距离的一种不对称变体。从 x 开始,我想知道将 x 变为 y 所需的最少删除和替换次数。

我可以只使用通常的 Levensthein 距离,还是我需要以某种方式修改算法?换句话说,对于通常的删除、替换和添加编辑集,删除两个字符串之间的长度差异然后再添加一些位是否有益?我怀疑答案是否定的,但我不确定。如果我错了,我确实需要修改 Levenshtein 距离的定义以禁止删除,我该怎么做?

最后,如果我从 y(较短的字符串)开始并且只允许添加和替换,我会直觉地期望我会得到相同的距离。这是正确的吗?我知道这些答案是什么,但我无法证明它们。

【问题讨论】:

  • 我认为常规的旧 Levenshtein 距离可以满足您所描述的需求。它可以处理长度不等的字符串。
  • 是的,我知道可以,这不是我要问的。我在问两个长度不等的字符串之间的 Levenshtein 距离是否有时可能与仅对两个字符串中较长的字符串应用删除和替换获得的距离不同。因此我的第二段。

标签: dynamic-programming levenshtein-distance edit-distance


【解决方案1】:

如果我理解正确,我认为答案是肯定的,Levenshtein 编辑距离可能与只允许删除和替换更大字符串的算法不同。因此,您需要修改或创建不同的算法来获得您的受限版本。

考虑两个字符串“ABCD”和“ACDEF”。 Levenshtein 距离为 3 (ABCD->ACD->ACDE->ACDEF)。如果我们从较长的字符串开始,并将自己限制为删除和替换,我们必须使用 4 次编辑(1 次删除和 3 次替换。原因是对较小字符串应用删除以有效获取较大字符串的字符串不能从较长的字符串开始时可以实现,因为它没有免费的插入操作(因为您不允许这样做)。

你的最后一段是真的。如果从较短到较长的路径只使用插入和替换,那么任何允许的路径都可以简单地从较长到较短的反转。无论方向如何,替换都是相同的,但是从小到大的插入在反转时变成了删除。

我尚未对此进行彻底测试,但此修改显示了我将采取的方向,并且似乎与我测试过的值一起使用。它是用 c# 编写的,并遵循 wikipedia 条目中关于 Levenshtein 距离的伪代码。可以进行明显的优化,但我没有这样做,因此我对标准算法所做的更改更加明显。一个重要的观察是(使用您的约束)如果字符串的长度相同,那么替换是唯一允许的操作。

    static int LevenshteinDistance(string s, string t) {
        int i, j;
        int m = s.Length;
        int n = t.Length;

        // for all i and j, d[i,j] will hold the Levenshtein distance between
        // the first i characters of s and the first j characters of t;
        // note that d has (m+1)*(n+1) values
        var d = new int[m + 1, n + 1];

        // set each element to zero
        // c# creates array already initialized to zero

        // source prefixes can be transformed into empty string by
        // dropping all characters
        for (i = 0; i <= m; i++) d[i, 0] = i;

        // target prefixes can be reached from empty source prefix
        // by inserting every character
        for (j = 0; j <= n; j++) d[0, j] = j;

        for (j = 1; j <= n; j++) {
            for (i = 1; i <= m; i++) {
                if (s[i - 1] == t[j - 1])
                    d[i, j] = d[i - 1, j - 1];       // no operation required
                else {
                    int del = d[i - 1, j] + 1;   // a deletion
                    int ins = d[i, j - 1] + 1;   // an insertion
                    int sub = d[i - 1, j - 1] + 1; // a substitution
                    // the next two lines are the modification I've made
                    //int insDel = (i < j) ? ins : del;
                    //d[i, j] = (i == j) ? sub : Math.Min(insDel, sub);
                    // the following 8 lines are a clearer version of the above 2 lines 
                    if (i == j) {
                        d[i, j] = sub;
                    } else {
                        int insDel;
                        if (i < j) insDel = ins; else insDel = del;
                        // assign the smaller of insDel or sub
                        d[i, j] = Math.Min(insDel, sub);
                    }
                }
            }
        }
        return d[m, n];
    }

【讨论】:

  • 谢谢你,我想你已经明白我的目的了。你能建议如何将Levensthein距离的常用动态规划算法调整为我正在寻找的算法吗?我遇到的麻烦是标准算法涉及计算所有子字符串之间的距离。但是,y 的某些子字符串将比 x 的某些子字符串,因此仅通过删除和替换将无法访问。
  • 谢谢!我还不能投票给答案,但我已经接受了。
  • 我并不是要贪婪,但是您认为您可以对末尾的两行进行伪代码来修改原始代码吗?我不懂 C#,但我可以学习其他所有内容。