我必须比较两个字符串,比如CDABAB
和EABACCCBB
。对我来说,相似性只是两者中相同字符的共同发生量,无论位置如何(即字符串是"文本"不是"序列")。此外,形成共同字符链的字符具有双重权重。
在我的示例中,A
,A
,B
,B
,C
是共同出现的实例。此外,ABA
是一个3个字符的双重链。因此,A(2)
,A(2)
,B(2)
,B(1)
,C(1)
总和,2+2+2+1+1=8
是两个字符串之间的相似之处。这个例子太容易了;在现实生活中,我有一千多个长度的字符串。所以我需要一个算法。
这显然是一个经典的任务问题。我知道它可以通过例如Hungarian algorithm来解决。我们应该构建共生矩阵(2 =由于链的双重):
E A B A C C C B B
C 1 1 1
D
A 2 1
B 2 1 1
A 2 2
B 2 1 1
并最佳地一对一地对行和列进行配对,以使对内相似度的总和最大化。它意味着在每行和每列中只留下一个正值,以一种给出最大矩阵值的方式。匈牙利算法是一种通用方法,可以给出这样的最大和(最优解)。左侧条目的总和为8
,与上面计算的相同:
然而,匈牙利语并不是特别快(而且编程也不是很容易)。我的长字符串需要另一种算法。 你能建议我另一种算法,更快更好吗?请注意,我的数据是特殊情况:它是相似性矩阵,只有3个整数值 - 0,1和2 。
(使用二进制矩阵,很容易做出最佳配对。有3个值,我知道怎么做,但它可以给出次优的,虽然好的"解决方案,是否有办法始终获得最佳效果?)
P.S。重要。我的字符串中的元素实际上不是有限字母表中的真实字符,而是真正的单词;所以 - 它们来自潜在的无限字母表,我事先并不知道。我不会进行频率计数等预处理。我倾向于从如上所示的矩形矩阵开始:我可以非常有效地制作具有适当的0,1,2值的矩阵。我的主要问题是进一步的算法。
谢谢(耐心等等)。
稍后更新:回复至@ j_random_hacker的回答。
我喜欢建议的快速方法,看起来很帅。但我发现这是有问题的。下面的例子演示了它。
Two strings are
S1= ABCACCDECF
S2= BACBCCDA
The matrix of co-occurences (where chains are given weight 2) is therefore:
B A C B C C D A
A 0 1 0 0 0 0 0 1*
B 1 0 0 2* 0 0 0 0
C 0 0 1 0 2* 1 0 0
A 0 2* 0 0 0 0 0 1
C 0 0 2* 0 2 1 0 0
C 0 0 1 0 1 2* 0 0
D 0 0 0 0 0 0 2* 0
E 0 0 0 0 0 0 0 0
C 0 0 1 0 1 1 0 0
F 0* 0 0 0 0 0 0 0
Hungarian algorithm of optimal pairing paired the rows and columns so as to maximize
the sum of within-pair values. These values - which pair a row and a column - are shown starred
in the matrix. And the sum is 13. It is the "similarity" between the strings.
Let us compute now the similarity by the quick algorithm suggested by @j_random_hacker.
Counts of individual characters (only occuring in both strings shown):
A B C D
S1 2 1 4 1
S2 2 2 3 1
---------------
Min 2 1 3 1
Sum1=7
Counts of dyadic chains (only occuring in both strings shown):
BC AC CC CD
S1 1 1 1 1
S2 1 1 1 1
-------------------
Min 1 1 1 1
Sum2=4
Counts of triadic chains (only occuring in both strings shown):
CCD
S1 1
S2 1
--------
Min 1
Sum3=1
Sum1 + 2Sum2 - Sum3 = 14. While I was expecting 13, as Hungarian algo gave.
The cause of the discrepancy seems obvious enough.
The second algorithm recognized 4 dyads: AC, BC, CD, and CC; and because CC and CD actually
superimpose by C (they form a triad CCD), C should be counted only once as a member of a dyad.
However, as seen from the starred elements in the matrix, Hungarian algorithm recognized
only 3 dyads: AC, BC, CD. Because the first of the two C is shared by by AC and CC or by
BC and CC, this character is (since it cannot be counted twice) is discarded altogether,
and hence the dyad it is a member of, CC, does not exist. And that is correct for me.
答案 0 :(得分:1)
[2014年3月23日编辑:这个答案不正确 - 请参阅OP顶部的反例。]
实际上这根本不是作业问题。
按如下方式预处理每个字符串S:
(请参阅下面的可能的加速。)
[编辑:已修复以下公式,以计算共享字母对中每个字母。]
任何一对弦S和T的相似性由A + 2B-C给出,其中
A = sum(min(f1[S][X], f1[T][X])) over all letters X
B = sum(min(f2[S][XY], f2[T][XY])) over all letter pairs XY
C = sum(min(f3[S][XYZ], f3[T][XYZ])) over all letter triples XYZ
A计算S和T共享的字母数,B计算相邻字母对的数量,C校正2个共享相邻对重叠1个字母的情况(例如,如果S和T都是{{1} },然后A = 3,B = 2(由于ABC
和AB
出现在两个字符串中)和C = 1,因此这两对共享的BC
不会出现这种情况。得到两次。)
如果字母大小很大,可能会有很多字母对甚至更多的三元组。在这种情况下,您可以添加在S"中至少出现一次的"或者"在T"中出现至少一次;到定义A,B和C的每一行的末尾 - 也就是说,它足以向前扫描其中一个字符串,查看其中出现的每个(唯一)字母,字母对和字母三元组。这将比较算法从字母表大小的立方体改变为较小的字符串的长度的线性。类似地,在预处理阶段,您只能使用哈希表而不是普通数组存储f1 [S],f2 [S]和f3 [S]中实际出现的字母,对和三元组。