类似的String算法

时间:2009-01-16 20:34:28

标签: c++ c algorithm string

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

就像这里提出的问题:Algorithm to find articles with similar text,区别在于我的文字字符串只会是少数几个字。

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

我正在寻找一种可用于匹配两者中文本的算法,并决定它们的匹配程度。在我的情况下,拼写和标点符号将是重要的。我不希望它们影响发现真实文本的能力。在上面的例子中,如果颜色参考被存储为“'天蓝色'”,我希望它仍然能够匹配。但是,列出的第3个字符串应该比第二个更好,

我敢肯定像谷歌这样的地方可能会使用类似于“你是不是的意思:”的功能......

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

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

9 个答案:

答案 0 :(得分:16)

Levenshtein距离不会完全奏效,因为你想允许重新排列。我认为你最好的选择是找到levenstein距离的最佳重排作为每个单词的成本。

要查找重新排列的费用,有点像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)

无论如何,你做成本函数的第二选择是最低成本,这正是你所期望的!

答案 1 :(得分:14)

确定“不依赖于订单的整体相似度”度量的一种方法是使用某种基于压缩的距离。基本上,大多数压缩算法(例如gzip)工作的方式是沿着字符串扫描,寻找之前出现的字符串片段 - 无论何时找到这样的片段,它都被替换为(偏移量,长度)识别要使用的早期段的对。您可以使用两个字符串压缩的度量来检测它们之间的相似性。

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

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

其中.被认为是连接。我们的想法是,通过首先查看t,您可以衡量进一步可以压缩s的数量。如果s == t,那么len(comp(s . t))几乎不会大于len(comp(s)),您将获得高分,而如果它们完全不同,len(comp(s . t))将非常接近len(comp(s) + comp(t)) {1}}你得分近零。中级相似度产生中间分数。

实际上,下面的公式更好,因为它是对称的(即分数不会改变,具体取决于哪个字符串是s,哪个是t):

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

这种技术源于信息理论。

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

答案 2 :(得分:5)

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

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

答案 3 :(得分:5)

您可能希望研究生物学家用来比较DNA序列的算法,因为它们必须处理许多相同的事情(块可能丢失,或者已插入,或者只是移动到不同的位置)字符串。

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

答案 4 :(得分:2)

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

答案 5 :(得分:2)

我遇到了类似的问题,我需要在字符串中获得相似的字符百分比。它需要精确的序列,所以例如“你好先生”和“先生你好”,比较需要给我五个相同的字符,在这种情况下,它们将是两个“你好”。然后它将花费两个字符串中最长的字符串的长度,并给出一个它们有多相似的百分比。这是我提出的代码

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));
}

答案 6 :(得分:1)

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

答案 7 :(得分:0)

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

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

要做除词汇相似性以外的任何事情,你需要查看自然语言处理,并且不会有单一的算法可以解决你的问题。

答案 8 :(得分:-2)

可能的方法:

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

当你这样做时,这里会有重复:对于每个“word1 | word2”字典条目,将会有一个“word2 | word1”条目,其中包含相同的距离值列表,但是否定。

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

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

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

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

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

我绝对没有这个代码,我可能只是重新发明了一个非常粗糙的轮子。 YMMV。