Algorithm 获取最接近的字符串匹配

Algorithm 获取最接近的字符串匹配,algorithm,language-agnostic,string-comparison,levenshtein-distance,Algorithm,Language Agnostic,String Comparison,Levenshtein Distance,我需要一种方法将多个字符串与测试字符串进行比较,并返回与之非常相似的字符串: TEST STRING: THE BROWN FOX JUMPED OVER THE RED COW CHOICE A : THE RED COW JUMPED OVER THE GREEN CHICKEN CHOICE B : THE RED COW JUMPED OVER THE RED COW CHOICE C : THE RED FOX JUMPED OVER THE BROWN COW (如果

我需要一种方法将多个字符串与测试字符串进行比较,并返回与之非常相似的字符串:

TEST STRING: THE BROWN FOX JUMPED OVER THE RED COW

CHOICE A   : THE RED COW JUMPED OVER THE GREEN CHICKEN
CHOICE B   : THE RED COW JUMPED OVER THE RED COW
CHOICE C   : THE RED FOX JUMPED OVER THE BROWN COW
(如果我做得正确的话)最接近“测试字符串”的字符串应该是“选项C”。最简单的方法是什么


我计划将其实现为多种语言,包括VB.net、Lua和JavaScript。此时,伪代码是可以接受的。如果你能为一种特定的语言提供一个例子,这也是非常感谢的

我认为选项B更接近测试字符串,因为它与原始字符串只有4个字符(删除2个字符)。而你认为C更接近,因为它包括棕色和红色。但是,它将具有更大的编辑距离

有一种称为的算法,用于测量两个输入之间的编辑距离

是该算法的工具

  • 将选择A的距离定为15
  • 将选项B的距离定为6
  • 将选项C的距离定为9

  • 编辑:对不起,我一直在levenshtein工具中混合字符串。更新为正确答案。

    大约一年前,当我在一个杂项信息数据库中查找用户输入的关于石油钻机的信息时,我遇到了这个问题。其目标是进行某种模糊字符串搜索,以识别具有最常见元素的数据库条目

    这项研究的一部分涉及到实现该算法,该算法确定要将一个字符串或短语转换为另一个字符串或短语,必须对其进行多少更改

    我提出的实现相对简单,包括两个短语的长度、每个短语之间的变化数量以及是否可以在目标条目中找到每个单词的加权比较

    这篇文章是在一个私人网站上,所以我会尽我所能在这里附加相关内容:


    模糊字符串匹配是对两个单词或短语的相似性进行类人估计的过程。在许多情况下,它涉及到识别彼此最相似的单词或短语。本文描述了模糊字符串匹配问题的内部解决方案,以及它在解决各种问题时的有用性,这些问题允许我们自动化以前需要用户参与的繁琐任务

    导言

    进行模糊字符串匹配的需要最初是在开发墨西哥湾验证工具时产生的。现有的是一个已知墨西哥湾石油钻塔和平台的数据库,购买保险的人会给我们一些关于他们资产的糟糕信息,我们必须将其与已知平台的数据库进行匹配。当提供的信息很少时,我们能做的最好的事情就是依靠承销商“识别”他们所指的信息,并调出适当的信息。这就是这个自动化解决方案派上用场的地方

    我花了一天时间研究模糊字符串匹配的方法,最终在维基百科上偶然发现了非常有用的Levenshtein距离算法

    实施

    在阅读了它背后的理论之后,我实施并找到了优化它的方法。这是我的代码在VBA中的外观:

    '计算两个字符串之间的Levenshtein距离(插入的数量,
    '将第一个字符串转换为第二个字符串所需的删除和替换)
    公共函数levenshteindication(ByRef S1作为字符串,ByVal S2作为字符串)的长度
    Dim L1为长,L2为长,D()为长输入字符串和距离矩阵的长度
    Dim i为Long,j为Long,cost为Long'循环计数器和替换当前字母的成本
    Dim cI为Long,cD为Long,cS为Long“下一次插入、删除和替换的成本
    L1=Len(S1):L2=Len(S2)
    重拨D(0到L1,0到L2)
    对于i=0到L1:D(i,0)=i:Next i
    对于j=0到L2:D(0,j)=j:Next j
    对于j=1到L2
    对于i=1到L1
    成本=资产负债表(StrComp(中间美元(S1,i,1),中间美元(S2,j,1),vbTextCompare))
    cI=D(i-1,j)+1
    cD=D(i,j-1)+1
    cS=D(i-1,j-1)+成本
    
    如果cILua实施,为子孙后代:

    function levenshtein_distance(str1, str2)
        local len1, len2 = #str1, #str2
        local char1, char2, distance = {}, {}, {}
        str1:gsub('.', function (c) table.insert(char1, c) end)
        str2:gsub('.', function (c) table.insert(char2, c) end)
        for i = 0, len1 do distance[i] = {} end
        for i = 0, len1 do distance[i][0] = i end
        for i = 0, len2 do distance[0][i] = i end
        for i = 1, len1 do
            for j = 1, len2 do
                distance[i][j] = math.min(
                    distance[i-1][j  ] + 1,
                    distance[i  ][j-1] + 1,
                    distance[i-1][j-1] + (char1[i] == char2[j] and 0 or 1)
                    )
            end
        end
        return distance[len1][len2]
    end
    

    你可能对这篇博文感兴趣

    FuzzyWzzy是一个Python库,它提供了简单的距离度量,例如用于字符串匹配的Levenshtein距离。它构建在标准库中的difflib之上,并将使用C实现Python levenshtein(如果可用)


    这个问题在生物信息学中经常出现。上述公认的答案(顺便说一句,非常好)在生物信息学中被称为Needleman-Wunsch(比较两个字符串)和Smith-Waterman(在更长的字符串中找到近似的子字符串)算法。他们工作得很好,几十年来一直是主力军

    但是如果有一百万个字符串要比较呢?这是一万亿个成对比较,每个都是O(n*m)!现代DNA测序仪很容易产生10亿个短DNA序列,每个序列大约有200个DNA“字母”长。通常,我们希望找到每个这样的字符串与人类基因组(30亿个字母)的最佳匹配。显然,Needleman-Wunsch算法及其相关算法不会这样做

    这个所谓的“对齐问题”是一个活跃的研究领域。目前最流行的算法能够在合理的硬件(例如,8核和32 GB RAM)上在数小时内找到10亿个短字符串和人类基因组之间的不精确匹配

    大多数算法都是通过快速查找短的精确匹配(种子),然后使用较慢的算法(例如Smith-Waterman)将其扩展到整个字符串。之所以这样做,是因为我们只对几个相近的匹配感兴趣,所以去掉99.9%没有共同点的配对是值得的

    如何找到精确匹配
    function levenshtein_distance(str1, str2)
        local len1, len2 = #str1, #str2
        local char1, char2, distance = {}, {}, {}
        str1:gsub('.', function (c) table.insert(char1, c) end)
        str2:gsub('.', function (c) table.insert(char2, c) end)
        for i = 0, len1 do distance[i] = {} end
        for i = 0, len1 do distance[i][0] = i end
        for i = 0, len2 do distance[0][i] = i end
        for i = 1, len1 do
            for j = 1, len2 do
                distance[i][j] = math.min(
                    distance[i-1][j  ] + 1,
                    distance[i  ][j-1] + 1,
                    distance[i-1][j-1] + (char1[i] == char2[j] and 0 or 1)
                    )
            end
        end
        return distance[len1][len2]
    end
    
    Example: x = HILLARY, y = HILARI(query term)
    Qgrams
    $$HILLARY$$ -> $$H, $HI, HIL, ILL, LLA, LAR, ARY, RY$, Y$$
    $$HILARI$$ -> $$H, $HI, HIL, ILA, LAR, ARI, RI$, I$$
    number of q-grams in common = 4
    
    var res = client.Search<ClassName>(s => s
        .Query(q => q
        .Match(m => m
            .Field(f => f.VariableName)
            .Query("SAMPLE QUERY")
            .Fuzziness(Fuzziness.EditDistance(5))
        )
    ));
    
    public static void Main()
    {
        Console.WriteLine("Hello World " + LevenshteinDistance("Hello","World"));
        Console.WriteLine("Choice A " + LevenshteinDistance("THE BROWN FOX JUMPED OVER THE RED COW","THE RED COW JUMPED OVER THE GREEN CHICKEN"));
        Console.WriteLine("Choice B " + LevenshteinDistance("THE BROWN FOX JUMPED OVER THE RED COW","THE RED COW JUMPED OVER THE RED COW"));
        Console.WriteLine("Choice C " + LevenshteinDistance("THE BROWN FOX JUMPED OVER THE RED COW","THE RED FOX JUMPED OVER THE BROWN COW"));
    }
    
    public static float LevenshteinDistance(string a, string b)
    {
        var rowLen = a.Length;
        var colLen = b.Length;
        var maxLen = Math.Max(rowLen, colLen);
    
        // Step 1
        if (rowLen == 0 || colLen == 0)
        {
            return maxLen;
        }
    
        /// Create the two vectors
        var v0 = new int[rowLen + 1];
        var v1 = new int[rowLen + 1];
    
        /// Step 2
        /// Initialize the first vector
        for (var i = 1; i <= rowLen; i++)
        {
            v0[i] = i;
        }
    
        // Step 3
        /// For each column
        for (var j = 1; j <= colLen; j++)
        {
            /// Set the 0'th element to the column number
            v1[0] = j;
    
            // Step 4
            /// For each row
            for (var i = 1; i <= rowLen; i++)
            {
                // Step 5
                var cost = (a[i - 1] == b[j - 1]) ? 0 : 1;
    
                // Step 6
                /// Find minimum
                v1[i] = Math.Min(v0[i] + 1, Math.Min(v1[i - 1] + 1, v0[i - 1] + cost));
            }
    
            /// Swap the vectors
            var vTmp = v0;
            v0 = v1;
            v1 = vTmp;
        }
    
        // Step 7
        /// The vectors were swapped one last time at the end of the last loop,
        /// that is why the result is now in v0 rather than in v1
        return v0[rowLen];
    }
    
    Hello World 4
    Choice A 15
    Choice B 6
    Choice C 8