3、编辑距离算法:从定义到纠错实战

好,咱们今天聊一个搜索引擎里特别实在的东西——编辑距离。

你想想看,用户输入「苹国」的时候,系统怎么知道他想搜的是「苹果」?说白了,就是靠算两个词之间的「距离」。这个距离越小,越有可能是拼写错误。

3.1 编辑距离的定义

编辑距离,英文叫 Edit Distance。它衡量的是:把一个字符串变成另一个字符串,最少需要多少次操作。

允许的操作就三种:

  • 插入:加一个字符
  • 删除:去掉一个字符
  • 替换:换成一个别的字符

举个例子。「cat」变成「cars」:

  1. 把 t 替换成 r → 「car」
  2. 再插入一个 s → 「cars」

两步搞定,编辑距离就是 2。

核心理解:编辑距离越小,两个词越像。搜索引擎纠错时,就是找编辑距离最小的那个正确词。

3.2 Levenshtein 距离——最经典的编辑距离

Levenshtein 距离是编辑距离里最常用的一种。它把三种操作都算作「一步」,权重都是 1。

我记得刚入行那会儿,有个同事问我:「为什么不用汉明距离?」我说:「汉明距离只能处理等长字符串,用户输入『苹国』和『苹果』长度都不一样,怎么比?」

Levenshtein 距离就不挑长度。它用动态规划,把两个字符串的所有对齐方式都算一遍,找出最小代价。

小提示:实际项目中,我习惯把替换操作的代价设成 2,插入和删除设成 1。因为替换往往意味着用户「看错了」,比多打或少打一个字母更严重。这个调参技巧,能显著提升纠错准确率。

3.3 动态规划实现——手写一个 Levenshtein

动态规划听起来高大上,其实就是一个填表的过程。我带你手写一遍。

假设我们要算「kitten」和「sitting」的距离。

先画一个 (m+1) × (n+1) 的表格。行是第一个词,列是第二个词。

     ""  s  i  t  t  i  n  g
""   0   1  2  3  4  5  6  7
k    1
i    2
t    3
t    4
e    5
n    6

填表规则很简单:

  • 如果两个字符相同,取左上角的值
  • 如果不同,取「左、上、左上」三个值的最小值 + 1

我直接上代码吧,这是我最常用的版本:

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

    # 初始化边界
    for i in range(m + 1):
        dp[i][0] = i
    for j in range(n + 1):
        dp[0][j] = j

    # 填表
    for i in range(1, m + 1):
        for j in range(1, n + 1):
            if s1[i - 1] == s2[j - 1]:
                dp[i][j] = dp[i - 1][j - 1]
            else:
                dp[i][j] = min(
                    dp[i - 1][j] + 1,    # 删除
                    dp[i][j - 1] + 1,    # 插入
                    dp[i - 1][j - 1] + 1 # 替换
                )
    return dp[m][n]

跑一下「kitten」和「sitting」:

""sitting
""01234567
k11234567
i22123456
t33212345
t44321234
e55432234
n66543323

右下角是 3。所以「kitten」到「sitting」需要 3 步。

注意:我曾经在线上环境踩过一个坑——直接用这个算法处理中文。中文一个汉字占 2-3 个字节,直接按字符算会出大问题。一定要先做分词或按 Unicode 码点处理。

3.4 编辑距离在纠错中的应用

理论讲完了,咱们聊聊怎么用。

搜索引擎纠错的流程一般是这样的:

  1. 用户输入查询词 Q
  2. 在词典里找所有与 Q 编辑距离 ≤ 2 的词
  3. 按编辑距离从小到大排序
  4. 取前 N 个作为候选纠错结果

我建议你设一个阈值。距离为 0 就是完全匹配,不用纠错。距离为 1 基本是笔误,直接改。距离为 2 可能是同音字或形近字,需要结合上下文判断。

举个例子。用户搜「苹国」:

  • 和「苹果」距离 = 1(把「国」换成「果」)
  • 和「平果」距离 = 1(把「苹」换成「平」)
  • 和「苹果园」距离 = 2(插入「园」,替换「国」)

距离最小的就是「苹果」和「平果」。再结合搜索热度,大概率选「苹果」。

实战经验:我在做电商搜索时发现,用户搜「苹国」有 80% 是想买苹果手机,只有 20% 是水果。所以不能只看编辑距离,还得加权重——热门词优先。

另外,性能问题也要考虑。词典如果有 100 万个词,每个查询都算一遍编辑距离,那服务器得炸。我常用的优化手段:

  • 前缀过滤:只算首字母相同的词
  • 长度过滤:只算长度差 ≤ 2 的词
  • BK 树:用树结构加速查找,能把 O(n) 降到 O(log n)

嗯,这些优化技巧咱们后面章节会细讲。今天先把编辑距离吃透。

最后总结一句:编辑距离是搜索引擎纠错的基石。你把它搞明白了,后面学什么模糊匹配、拼音纠错,都会轻松很多。