字符串相似度算法?

Posted

技术标签:

【中文标题】字符串相似度算法?【英文标题】:String similarity algorithms? 【发布时间】:2011-04-04 07:44:44 【问题描述】:

我需要比较 2 个字符串并计算它们的相似度,以筛选出最相似的字符串列表。

例如。搜索“狗”会返回

    狗 狗狗 沼泽 雾 有雾

例如。搜索“crack”会返回

    破解 俏皮话 机架 杰克 嘎嘎

我遇到过:

QuickSilver LiquidMetal

你知道更多的字符串相似度算法吗?

【问题讨论】:

社区维基,因为您的问题没有正确答案 A better similarity ranking algorithm for variable length strings的可能重复 负载 的问题已经在处理正是这个主题。提问前请先搜索。 @j_random_hacker:你说很多问题?将我链接到一个演示至少一种新技术的问题?在发布我的问题之前,我已经看过你链接的那个。我不想做任何简单的排名或排序,而是一个非常准确的相似性算法,如果我搜索字典数据库,它会返回我显示的结果。 有很多方法可以衡量相似度,你没有确切解释你在寻找什么类型,但根据你的例子和你不喜欢 Levenshtein 距离的事实,我认为你'在某种近似子串匹配算法之后。您问题的变体是algorithm 标签下的 最受欢迎的主题。以下是一些链接: 【参考方案1】:
class Program  
    static int ComputeLevenshteinDistance(string source, string target) 
        if ((source == null) || (target == null)) return 0;
        if ((source.Length == 0) || (target.Length == 0)) return 0;
        if (source == target) return source.Length;

        int sourceWordCount = source.Length;
        int targetWordCount = target.Length;

        int[,] distance = new int[sourceWordCount + 1, targetWordCount + 1];

        // Step 2
        for (int i = 0; i <= sourceWordCount; distance[i, 0] = i++);
        for (int j = 0; j <= targetWordCount; distance[0, j] = j++);

        for (int i = 1; i <= sourceWordCount; i++) 
            for (int j = 1; j <= targetWordCount; j++) 
                // Step 3
                int cost = (target[j - 1] == source[i - 1]) ? 0 : 1;

                // Step 4
                distance[i, j] = Math.Min(Math.Min(distance[i - 1, j] + 1, distance[i, j - 1] + 1), distance[i - 1, j - 1] + cost);
            
        

        return distance[sourceWordCount, targetWordCount]; 
    

    static void Main(string[] args) 
       Console.WriteLine(ComputeLevenshteinDistance ("***","StuckOverflow"));
       Console.ReadKey();
    

【讨论】:

我认为他要求的是算法而不是解决方案的实现。【参考方案2】:

Levenshtein 距离是我推荐的算法。它计算将一个字符串更改为另一个字符串所必须执行的最小操作数。更改越少意味着字符串越相似...

【讨论】:

Levenshtein 距离及其所有排列(例如 Dam-Lev)的效果非常糟糕,甚至 QuickSilver 在最基本的比较中也超过了它。见***.com/questions/3338889/… @Jenko:你说 Levenshtein 距离的效果“非常糟糕”,但你没有给出任何标准来决定什么是好是坏。考虑到 Levenshtein 距离几乎是典型的“字符串相似性算法”,你应该让你的问题更具体。 @j_random_hacker:编辑您的帖子以说明原因。我将您链接到包含相同结果的问题,为什么您没有阅读我不理解的内容。 @Jenko:(1) 这不是我的帖子。 (2) “不稳定”不是一个有意义的标准。我知道您对结果不满意,但您需要准确解释您正在寻找的相似性类型。顺便说一句,您通常会设置 Lev 距离的上限,以便在您的示例中只返回答案 1-3。 我正在将 levenshtein distance 用于我现在正在进行的项目,但事实证明它对于这个用例来说并不理想。我发现知道一行中有多少个字母匹配与以相同的顺序匹配字符串中的相同字母同样重要,这基本上就是编辑距离的作用。【参考方案3】:

看来您需要某种模糊匹配。这是一组相似性指标http://www.dcs.shef.ac.uk/~sam/stringmetrics.html 的java 实现。这里是对字符串指标http://www.cs.cmu.edu/~wcohen/postscript/ijcai-ws-2003.pdf 的更详细说明,这取决于您的实现必须有多模糊和多快。

【讨论】:

@PascalKlein Wayback Machine 上提供了一个存档页面。我已将链接更新为web.archive.org/web/20081224234350/http://www.dcs.shef.ac.uk/… 有 levenshtein,您可以尝试使用类似 Wu-Palmer (wup) 之类的相似性分数进行修剪,该分数使用受人尊敬的 Wordnet。用于 Java 的斯坦福 NLP 是一个 goto。还有pattern,scipy,numpy;用于 Python 的 gensim。最好通过矩阵的对角线计算 Levenshtein。【参考方案4】:

如果重点是性能,我会实现一个基于trie 结构的算法 (可以很好地在文本中查找单词,或帮助纠正单词,但在您的情况下,您可以快速找到包含给定单词或除一个字母之外的所有单词的所有单词)。

请先看上面的***链接。Tries是最快的单词排序方法(n个单词,搜索s,O(n) 创建树,O(1) 搜索 s (或者如果您愿意,如果 a 是平均长度,O(an) 用于 trie,O(s) 用于搜索))。

您的问题(相似词)的快速简便的实现(待优化)包括

用单词列表制作trie,将所有字母的前后索引(参见下面的示例) 要搜索 s,从 s[0] 迭代以在 trie 中查找单词,然后是 s[1] 等。 . 在 trie 中,如果找到的字母数为 len(s)-k,则显示单词,其中 k 是容差(缺少 1 个字母,2...)。 算法可以扩展到列表中的单词(见下文)

例如,用词carvars

构建 trie(大写字母表示一个词在这里结束,而另一个可能继续)。 &gt; 是索引后(前进),&lt; 是索引前(后退)。在另一个示例中,我们可能还必须指出起始字母,为清楚起见,此处未显示。 例如,C++ 中的&lt;&gt; 将是Mystruct *previous,*next,意思是从a &gt; c &lt; r,您可以直接从a 转到c,反之,也可以从a 转到R

  1.  c < a < R
  2.  a > c < R
  3.    > v < r < S
  4.  R > a > c
  5.        > v < S
  6.  v < a < r < S
  7.  S > r > a > v

严格寻找 car,trie 让您可以从 1. 开始访问,然后您会找到 car(您还会发现以 car开头的所有内容>,还有里面有车的任何东西 - 它不在示例中 - 但是 vicar 例如可以从 c &gt; i &gt; v &lt; a &lt; R) 找到。

要在允许 1 个字母的错误/缺失容差的情况下进行搜索,请从 s 的每个字母进行迭代,并计算从 获得的连续字母的数量 - 或跳过 1 个字母>s 在树中。

寻找car

c:在树中搜索c &lt; ac &lt; rs 中缺少字母)。要接受单词 w 中的错误字母,请尝试在每次迭代时跳到错误的字母以查看 ar 是否在后面,这是 O(w)。有两个字母,O(w²) 等...但是可以将另一个级别的索引添加到 trie 以考虑 jump 字母 - 使 trie复杂且贪婪的内存。 a,然后是r:同上,但也向后搜索

这只是为了提供一个关于原理的想法——上面的例子可能有一些小故障(我明天再检查一下)。

【讨论】:

【参考方案5】:

你可以这样做:

Foreach string in haystack 偏移量 := -1; matchedCharacters := 0; Foreach char in needle offset := PositionInString(string, char, offset+1); 如果 偏移量 = -1 那么 休息; 结束; matchedCharacters := matchedCharacters + 1; 结束; 如果 匹配字符 > 0 那么 //(部分)匹配找到 结束; 结束;

使用matchedCharacters,您可以确定匹配的“程度”。如果它等于needle的长度,那么needle中的所有字符也在string中。如果你还存储了第一个匹配字符的偏移量,你还可以通过从最后一个匹配字符的偏移量中减去第一个匹配字符的偏移量,按照匹配字符的“密度”对结果进行排序offset时间>;差异越小,匹配越密集。

【讨论】:

@Jenko:你什么意思?搜索是线性的,因此字符串列表中的每个字符串都会被测试。 PositionInString 是什么意思? @MoritzSchmitzv.Hülst PositionInString 是一个函数,它返回从 开始的 stringchar 的索引位置偏移量.

以上是关于字符串相似度算法?的主要内容,如果未能解决你的问题,请参考以下文章

字符串相似度算法?

算法8 字符串相似度

文本、语音相似度算法

这已经是字符串相似度算法了吗?

java算法---余弦相似度计算字符串相似率

关于文本相似度-LD算法和余弦算法的比较