如何解决最优对齐的字符串相似度
算法的预期行为
我有两个字符串 a
和 b
,其中 a
是较短的字符串。我想找到与 b
相似度最大的 a
子串。子串必须是 len(a)
,或者必须放在 b
的末尾。
例如对于以下两个字符串:
a = "aa"
b = "bbaba"
b 的可能子串是
"bb"
"ba"
"ab"
"ba"
"a"
""
编辑距离定义为插入和删除的数量。替换是不可能的(必须使用插入 + 删除)。根据以下等式计算两个字符串之间的相似度:norm = 1 - distance / (len(a) + len(substring))
。
所以上面的子字符串将提供以下结果:
"bb" -> 2 DEL + 2 INS -> 1 - 4 / 4 = 0
"ba" -> 1 DEL + 1 INS -> 1 - 2 / 4 = 0.5
"ab" -> 1 DEL + 1 INS -> 1 - 2 / 4 = 0.5
"ba" -> 1 DEL + 1 INS -> 1 - 2 / 4 = 0.5
"a" -> 1 INS -> 1 - 1 / 3 = 0.66
"" -> 2 INS -> 1 - 2 / 2 = 0
所以算法应该返回 0.66。
不同的实现
类似的比例由 Python 库 fuzzywuzzy 以 fuzz.partial_ratio
的形式实现。它分两步计算比率:
-
使用 difflib.SequenceMatcher.get_matching_blocks 搜索较长序列中的匹配子序列
-
从匹配的子序列开始计算 len(shorter_string) 的子字符串的比率并返回最大比率
这真的很慢,所以它在可用时使用python-Levenshtein进行此相似度计算。这会根据 Levenshtein 距离执行相同的计算,但速度更快。然而,在边缘情况下,用于比率计算的计算匹配块是完全错误的(参见 issue 16),当正确性相关时,这不会使其成为合适的替代品。
当前实现
我目前将 difflib 的 C++ 端口与权重插入 = 1、删除 = 1 和替换 = 2 的 Levenshtein 距离的快速位并行实现结合使用。当前的实现可以在这里找到:
- 提取匹配块:matching_blocks
- 计算加权 Levenshtein:weighted Levenshtein
- 将它们结合起来计算最终比率:partial_ratio
问题
有没有更快的算法来计算这种相似度。要求是:
- 仅使用替换/插入(或赋予替换权重为 2,具有类似效果)
- 允许在较长字符串的开头有一个间隙
- 允许在较长字符串的末尾有一个间隙,只要剩余的子字符串不会变得比较短字符串的长度更短。
- 它强制执行,子串具有相似的长度(当它不在最后时),因此它匹配 fuzzywuzzy 的行为,但是当它允许匹配更长的子序列时也可以:例如对于
aaba
:aaa
这意味着允许使用aaba
而不是aab
作为最佳子序列。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。