比较字符串相似度

68

什么是比较两个字符串相似程度的最佳方法?

示例:

My String
My String With Extra Words

或者

My String
My Slightly Different String

我希望确定每对字符串中的第一个和第二个字符串有多相似。我想对比它们,并为这个比较打分。如果这些字符串足够相似,我将认为它们是匹配的一对。

在C#中有好的方法吗?


3
Levenshtein编辑距离、Soundex和汉明距离都以不同的方式实现这一点。在寻找实现方法之前,您需要更好地定义您的度量标准。 - bmm6o
6
如果有其他人偶然看到这个问题,请考虑访问https://github.com/DanHarltey/Fastenshtein。 - Mugen
请问是否有为C编写的模糊搜索或字符串相似度函数库?相关信息请参考:https://dev59.com/1HVD5IYBdhLWcg3wHnsX - StayOnTarget
3个回答

109
static class LevenshteinDistance
{
    public static int Compute(string s, string t)
    {
        if (string.IsNullOrEmpty(s))
        {
            if (string.IsNullOrEmpty(t))
                return 0;
            return t.Length;
        }

        if (string.IsNullOrEmpty(t))
        {
            return s.Length;
        }

        int n = s.Length;
        int m = t.Length;
        int[,] d = new int[n + 1, m + 1];

        // initialize the top and right of the table to 0, 1, 2, ...
        for (int i = 0; i <= n; d[i, 0] = i++);
        for (int j = 1; j <= m; d[0, j] = j++);

        for (int i = 1; i <= n; i++)
        {
            for (int j = 1; j <= m; j++)
            {
                int cost = (t[j - 1] == s[i - 1]) ? 0 : 1;
                int min1 = d[i - 1, j] + 1;
                int min2 = d[i, j - 1] + 1;
                int min3 = d[i - 1, j - 1] + cost;
                d[i, j] = Math.Min(Math.Min(min1, min2), min3);
            }
        }
        return d[n, m];
    }
}

5
这将是我的答案。达梅罗-勒文斯坦距离算法计算将一个字符串转换为另一个字符串所需的字母添加、删除、替换和换位(交换)操作的数量。分数越低,它们越相似。 - KeithS
1
需要注意的是,即使对于中等大小的字符串,这种方法也非常占用内存。有一个简单的解决方案,只需要额外的 min(n, m) + 1 内存即可。 - Konrad Rudolph
1
这个很好用。幸运的是,我的所有字符串都非常短(50个字符或更少),所以它对我来说处理非常快。 - Brandon
1
更快的实现在这里:http://web.archive.org/web/20120526085419/http://www.merriampark.com/ldjava.htm。我运行的一些测试从30-50秒降至8-10秒。 - Frank Schwieterman
@FrankSchwieterman 不要存储完整的矩阵,只需存储前一列向量以及当前列的上一行对应的单个字段 prev(因此为 +1)。在给定行 i 中,向量中从 0 到 (i-1) 的所有值都对应于更新后的值。也就是说,在循环中的赋值语句为 prev = d[i]; d[i] = Math.Min(…);。值得注意的是,这比您在更新评论中链接的实现方式更好 - Konrad Rudolph

11

如果有人想知道@FrankSchwieterman发布的内容在C#中的等效写法:

public static int GetDamerauLevenshteinDistance(string s, string t)
{
    if (string.IsNullOrEmpty(s))
    {
        throw new ArgumentNullException(s, "String Cannot Be Null Or Empty");
    }

    if (string.IsNullOrEmpty(t))
    {
        throw new ArgumentNullException(t, "String Cannot Be Null Or Empty");
    }

    int n = s.Length; // length of s
    int m = t.Length; // length of t

    if (n == 0)
    {
        return m;
    }

    if (m == 0)
    {
        return n;
    }

    int[] p = new int[n + 1]; //'previous' cost array, horizontally
    int[] d = new int[n + 1]; // cost array, horizontally

    // indexes into strings s and t
    int i; // iterates through s
    int j; // iterates through t

    for (i = 0; i <= n; i++)
    {
        p[i] = i;
    }

    for (j = 1; j <= m; j++)
    {
        char tJ = t[j - 1]; // jth character of t
        d[0] = j;

        for (i = 1; i <= n; i++)
        {
            int cost = s[i - 1] == tJ ? 0 : 1; // cost
            // minimum of cell to the left+1, to the top+1, diagonally left and up +cost                
            d[i] = Math.Min(Math.Min(d[i - 1] + 1, p[i] + 1), p[i - 1] + cost);
        }

        // copy current distance counts to 'previous row' distance counts
        int[] dPlaceholder = p; //placeholder to assist in swapping p and d
        p = d;
        d = dPlaceholder;
    }

    // our last action in the above loop was to switch d and p, so p now 
    // actually has the most recent cost counts
    return p[n];
}

我认为s或t可能为空或为空字符串,因为如果两者相同,则差异为100%或无差异。我还会在一开始进行相等比较以查看它们是否相同。 - Walter Verhoeven

2
我正在这样比较两个句子。
string[] vs = string1.Split(new char[] { ' ', '-', '/', '(', ')' },StringSplitOptions.RemoveEmptyEntries);
string[] vs1 = string2.Split(new char[] { ' ', '-', '/', '(', ')' }, StringSplitOptions.RemoveEmptyEntries);


vs.Intersect(vs1, StringComparer.OrdinalIgnoreCase).Count();

Intersect功能会提供一组相同的词汇列表,通过统计次数,如果超过1次,说明这两个句子包含类似的单词。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接