Luke a Pro

Luke Sun

Developer & Marketer

🇺🇦
EN||

Damerau-Levenshtein 距离

| , 3 minutes reading.

Damerau-Levenshtein:键盘上的同理心

算法背后的故事:打字太快的代价

想象你正在输入 algorithm,但你的手指动得太快了,以至于打成了 algorit hm(你交换了 hm 的位置)。

在标准的 Levenshtein 距离 看来,这是 2 次操作(删除 h 再插入 h;或者两次替换)。但在人类的认知里,这只是 一个错误:一次 字符邻接交换 (Transposition)

1964 年,Frederick J. Damerau 发表了一篇论文。他观察到,在人类所有的拼写错误中,有 80% 是由以下四种情况组成的:一次插入、一次删除、一次替换,以及 相邻两个字符的交换

通过在 Levenshtein 的矩阵中加入“交换”规则,他创造了一个感觉更“人性化”的算法。它理解我们不是随机犯错的机器人,而是生物,我们的手指有时会跑在思想的前面。

为什么需要它?

Damerau-Levenshtein 是模糊匹配的“专业版”。

  • 搜索引擎: 它能为常见的打字失误(如将 the 打成 teh)提供更准确的建议。
  • OCR(光学字符识别): 修正扫描仪在识别时可能产生的相邻字符位置误判。
  • 生物信息学: 在某些生物学语境中,相邻的碱基可能会发生交换,该算法能捕捉到这种变异。

算法是如何“思考”的

它与 Levenshtein 几乎完全相同,但多了一份 “记忆”

  1. 标准的三个选项: 它依然会检查插入、删除和替换。
  2. 交换 (Transposition): 当编辑在矩阵中移动时,他会多往回看两个步骤。他会问:

    “当前字符是否等于另一个单词的前一个字符,且反之亦然?” 如果是,它就将 一次交换 视为一次操作,并计算其成本。

这让算法在逻辑上稍微复杂了一点,但对于处理人类产生的文本,它的准确性有了显著提升。

工程决策:“完美”的代价

和 Levenshtein 一样,它的复杂度也是 O(NM)O(N \cdot M)

  • 选择: 大多数开发者使用的是“受限”版本(Optimal String Alignment),它不允许在同一个子串上进行多次编辑。这对于拼写检查器来说已经足够快且好用。
  • “人”的因素: 如果你的应用是一个 CLI 工具或搜索框,用户经常需要快速输入,那么 Damerau-Levenshtein 值得你多写那几行逻辑。如果你是在比较机器生成的哈希值,那么 Levenshtein 或简单的位运算就足够了。

实现参考 (Python - 受限版本)

def damerau_levenshtein(s1, s2):
    n, m = len(s1), len(s2)
    dp = [[0] * (m + 1) for _ in range(n + 1)]

    for i in range(n + 1): dp[i][0] = i
    for j in range(m + 1): dp[0][j] = j

    for i in range(1, n + 1):
        for j in range(1, m + 1):
            cost = 0 if s1[i-1] == s2[j-1] else 1
            
            # 标准的 Levenshtein 编辑操作
            dp[i][j] = min(
                dp[i-1][j] + 1,      # 删除
                dp[i][j-1] + 1,      # 插入
                dp[i-1][j-1] + cost  # 替换
            )
            
            # 核心:Damerau 的相邻交换逻辑
            if i > 1 and j > 1 and s1[i-1] == s2[j-2] and s1[i-2] == s2[j-1]:
                # 检查是否可以通过一次交换来到达当前状态
                dp[i][j] = min(dp[i][j], dp[i-2][j-2] + cost)

    return dp[n][m]

# 使用示例
# print(damerau_levenshtein("teh", "the")) # 输出: 1
# print(damerau_levenshtein("ca", "abc")) # 输出: 3

小结

Damerau-Levenshtein 教会我们:机器想要变得聪明,就必须理解人类是如何失败的。通过识别两个手指的简单位置交换,我们弥合了冰冷的逻辑与人类生物学之间的鸿沟。它提醒我们,在代码的世界里,同理心往往只是动态规划矩阵中的第四个选项。