【问题标题】:Similar String algorithm类似的字符串算法
【发布时间】:2010-10-01 21:36:56
【问题描述】:

我正在寻找一种算法,或者至少是关于如何在两个或多个不同字符串中找到相似文本的操作理论...

很像这里提出的问题:Algorithm to find articles with similar text,不同之处在于我的文本字符串只会是几个单词。

就像说我有一个字符串: “进入湛蓝的天空” 我正在与以下两个字符串进行比较: “颜色是天蓝色”和 “在蔚蓝晴朗的天空中”

我正在寻找一种算法,可以用来匹配两者中的文本,并决定它们匹配的接近程度。就我而言,拼写和标点符号将很重要。我不希望它们影响发现真实文本的能力。在上面的示例中,如果颜色参考存储为“'sky-blue'”,我希望它仍然能够匹配。但是,列出的第三个字符串应该比第二个更好,等等。

我敢肯定,像 Google 这样的地方可能会使用与“您的意思是:”功能类似的东西...

* 编辑 *
在与一位朋友交谈时,他与一位撰写有关该主题的论文的人一起工作。我想我可能会与所有阅读本文的人分享它,因为其中描述了一些非常好的方法和过程......

这里是link to his paper,希望对阅读这个问题以及类似字符串算法的人有所帮助。

【问题讨论】:

    标签: c++ c algorithm string


    【解决方案1】:

    还有另一种方法。使用卷积的模式识别。图像 A 通过傅立叶变换运行。图 B 也是。现在将 F(A) 叠加在 F(B) 上,然后将其转换回给您一个带有一些白点的黑色图像。这些点表明 A 与 B 强烈匹配的位置。斑点的总和将表明总体相似性。不确定如何在字符串上运行 FFT,但我很确定它会起作用。

    【讨论】:

      【解决方案2】:

      我不能在这里标记两个答案,所以我要回答并标记我自己的答案。在大多数情况下,Levenshtein 距离似乎是正确的方法。但是,值得一提的是j_random_hackers 的回答。我已经使用 LZMA 的实现来测试他的理论,它被证明是一个合理的解决方案。在我最初的问题中,我正在寻找一种短字符串(2 到 200 个字符)的方法,Levenshtein 距离算法将在其中工作。但是,问题中没有提到需要比较两个(较大的)字符串(在这种情况下,是中等大小的文本文件)并执行快速检查以查看两者的相似程度。我相信这种压缩技术会很好地工作,但我还没有研究它,以确定在样本数据的大小和相关操作的速度/成本方面,哪一个变得比另一个更好。我认为这个问题的很多答案都很有价值,值得一提,对于任何想要解决像我在这里所做的类似字符串考验的人。谢谢大家的精彩回答,我希望它们也可以用来为他人服务。

      【讨论】:

        【解决方案3】:

        我遇到了类似的问题,我需要获取字符串中相似字符的百分比。它需要精确的序列,因此例如“hello sir”和“sir hello”在比较时需要给我五个相同的字符,在这种情况下它们将是两个“hello”。然后它将取两个字符串中最长的长度,并给出它们相似程度的百分比。这是我想出的代码

        int compare(string a, string b){
           return(a.size() > b.size() ? bigger(a,b) : bigger(b,a));
        }
        
        
        
        int bigger(string a, string b){
        
        
        
        int maxcount = 0, currentcount = 0;//used to see which set of concurrent characters were biggest
        
        for(int i = 0; i < a.size(); ++i){
        
            for(int j = 0; j < b.size(); ++j){
        
                if(a[i+j] == b[j]){
        
                 ++currentcount;
        
                 }
        
                else{
        
                    if(currentcount > maxcount){
        
                     maxcount = currentcount;
        
                     }//end if
        
                     currentcount = 0;
        
                    }//end else
        
                }//end inner for loop
        
            }//end outer for loop
        
        
           return ((int)(((float)maxcount/((float)a.size()))*100));
        }
        

        【讨论】:

          【解决方案4】:

          Levenshtein distance 不会完全起作用,因为您希望允许重新排列。我认为您最好的选择是找到以列文斯坦距离作为每个单词成本的最佳重新排列。

          要找到重新排列的成本,有点像pancake sorting problem。因此,您可以置换每个单词组合(过滤掉完全匹配),以及其他字符串的每个组合,尽量减少每个单词对上置换距离和 Levenshtein 距离的组合。

          编辑: 现在我有时间可以发布一个简单的示例(所有“最佳”猜测都在检查中,而不是实际运行算法):

          original strings             | best rearrangement w/ lev distance per word
          Into the clear blue sky      |    Into the c_lear blue sky 
          The color is sky blue        |    is__ the colo_r blue sky
          
          R_dist = dist( 3 1 2 5 4 ) --> 3 1 2 *4 5* --> *2 1 3* 4 5 --> *1 2* 3 4 5 = 3  
          L_dist = (2D+S) + (I+D+S) (Total Subsitutions: 2, deletions: 3, insertion: 1)  
          

          (注意所有翻转都包括范围内的所有元素,我使用 Xi - Xj = +/- 1 的范围)

          其他例子

          original strings             | best rearrangement w/ lev distance per word
          Into the clear blue sky      |   Into the clear blue sky 
          In the blue clear sky        |   In__ the clear blue sky
          
          R_dist = dist( 1 2 4 3 5 ) -->  1 2 *3 4* 5  = 1
          L_dist = (2D) (Total Subsitutions: 0, deletions: 2, insertion: 0)
          

          并显示这三者的所有可能组合...

          The color is sky blue         |    The colo_r is sky blue
          In the blue clear sky         |    the c_lear in sky blue
          
          R_dist = dist( 2 4 1 3 5 ) --> *2 3 1 4* 5 --> *1 3 2* 4 5 --> 1 *2 3* 4 5 = 3
          L_dist = (D+I+S) + (S) (Total Subsitutions: 2, deletions: 1, insertion: 1)
          

          无论如何,您使成本函数成为第二选择将是最低成本,这是您所期望的!

          【讨论】:

          • 哈——我也在那篇文章上回答了 Levenstein 距离:P 我不确定我是否足够聪明,是否能读懂你在那篇文章中引用的论文 0.o
          • 我还不确定 Levenstein 距离有什么作用,但是为了使结果“不考虑顺序”,您似乎可以在运行算法之前对顺序进行规范化。也许按字母顺序排列单词。 “蓝色就是天空” | “天蓝晴”。在某些情况下,它可能无济于事,只是一个想法。
          • lev distance 计算插入和删除的数量,以将一段文本转换为另一段。通过对句子进行排序,您不会对该排序增加惩罚或增加该排序的复杂性。所以,如果你不关心这个,那可能会起作用。
          【解决方案5】:

          确定“不考虑顺序的整体相似度”的一种方法是使用某种基于压缩的距离。基本上,大多数压缩算法(例如gzip)的工作方式是沿着字符串扫描以查找较早出现的字符串段——只要找到这样的段,它就会被替换为(偏移量,长度)对标识要使用的较早段。您可以使用衡量两个字符串的压缩程度来检测它们之间的相似性。

          假设您有一个函数string comp(string s),它返回一个压缩版本的s。然后,您可以使用以下表达式作为两个字符串 st 之间的“相似度分数”:

          len(comp(s)) + len(comp(t)) - len(comp(s . t))
          

          其中. 被视为串联。这个想法是,您可以通过首先查看s 来测量您可以进一步压缩t。如果s == t,那么len(comp(s . t)) 几乎不会比len(comp(s)) 大,你会得到一个高分,而如果它们完全不同,len(comp(s . t)) 将非常接近len(comp(s) + comp(t)),你会得到一个分数接近零。中等水平的相似性产生中等分数。

          实际上,以下公式更好,因为它是对称的(即分数不会根据哪个字符串是s 而哪个是t而改变):

          2 * (len(comp(s)) + len(comp(t))) - len(comp(s . t)) - len(comp(t . s))
          

          这种技术源于信息论。

          优点:良好的压缩算法已经可用,因此您无需进行大量编码,并且它们以线性时间(或接近线性时间)运行,因此速度很快。相比之下,涉及所有单词排列的解决方案在单词数量上呈超指数增长(尽管在您的情况下这可能不是问题,因为您说您知道只有少数单词)。

          【讨论】:

          • 我也喜欢这种方法!一定要把这片海放进我的工具箱里!!
          • 非常有趣的想法...我需要决定要使用的压缩算法。我会选择一些经过验证的真实的东西,比如放气,还是 LZ77 与 UDA?我想我想使用最擅长进行原始压缩、丢弃所有字典数据等的方法。还是 len() 的一部分?
          • Deflate 预先添加了一个霍夫曼表,因此对于简短的输入,它会给你在“绝对”意义上扭曲的分数,但它仍然保留 score(X, Y)
          • 最实用的压缩算法会输出一些初始标头信息,因此短输入的失真分数始终是一个问题,但如果重要的是能够检测到字符串 X 与几个可能的最佳匹配字符串 Y,这并不重要——只有相对分数很重要。
          • 您可能还需要考虑通过除以例如标准化分数len(comp(s))+len(comp(t))。
          【解决方案6】:

          困难在于在语义上匹配字符串。

          您可以根据字符串的词法属性生成某种值。例如他们的机器人有蓝色和天空,它们在同一个句子中,等等......但它不会处理“Sky's jean is blue”或其他一些使用相同单词的奇怪球英语结构的情况,但是你需要解析英语语法...

          要进行词汇相似性以外的任何事情,您需要研究自然语言处理,并且不会有一种单一的算法可以解决您的问题。

          【讨论】:

            【解决方案7】:

            可能的方法:

            reference 字符串中的所有单词组合构造一个字符串键为“word1|word2”的字典。单个组合可能会发生多次,因此 Dictionary 的值应该是一个 list 数字,每个数字代表参考字符串中单词之间的 距离

            当你这样做时,这里会出现重复:对于每个“word1|word2”字典条目,都会有一个“word2|word1”条目具有相同的距离值列表,但被否定了。

            对于 comparison 字符串中的每个单词组合(单词 1 和 2、单词 1 和 3、单词 2 和 3 等),检查两个键(word1|word2 和 word2| word1) 在参考字符串中找到与当前字符串中的距离最接近的 值。将当前距离与最近距离之差的绝对值添加到计数器。

            如果单词之间的最近参考距离与比较字符串的方向相反(word2|word1),您可能希望将其加权小于两个字符串中最接近的值在相同方向的情况。

            完成后,将总和除以比较字符串中单词数的平方。

            这应该提供一些十进制值,表示每个单词/短语与原始字符串中的某些单词/短语的匹配程度。

            当然,如果原始字符串较长,则不会考虑这一点,因此可能需要计算这两个方向(使用一个作为参考,然后是另一个)并将它们平均。

            我完全没有这方面的代码,而且我可能只是重新发明了一个非常粗糙的轮子。 YMMV。

            【讨论】:

              【解决方案8】:

              您可能需要研究生物学家用来比较 DNA 序列的算法,因为它们必须处理许多相同的事情(可能缺少块,或者被插入,或者只是移动到字符串。

              Smith-Waterman 算法是一个可能运行良好的示例,尽管它可能对您的使用来说太慢了。不过可能会给你一个起点。

              【讨论】:

                【解决方案9】:

                一种方法(尽管这可能更适合拼写检查类型的算法)是“编辑距离”,即计算将一个字符串转换为另一个字符串需要多少次编辑。在这里可以找到一种常见的技术:

                http://en.wikipedia.org/wiki/Levenshtein_distance

                【讨论】:

                • 谢谢。我要读一下这个。在我引用的另一个问题中提到了它,但我不确定这就是我要找的。我想我更多的是在寻找一种查看单词的算法,并采用 match-seach-match 类型的方法。
                猜你喜欢
                • 2018-08-12
                • 2013-02-24
                • 2011-04-04
                • 2010-09-08
                • 1970-01-01
                • 2016-07-11
                • 2022-07-08
                • 1970-01-01
                • 2017-12-23
                相关资源
                最近更新 更多