词汇/表达差异-1-编辑距离-莱文斯坦距离-Levenshtein

1.基本原理

其核心思想是:将一个字符串转换为另一个字符串所需的最少单字符编辑操作次数。

**编辑距离(Edit Distance)**衡量的是:

把字符串 A 转换成字符串 B 所需的最小编辑操作代价

最经典的是 Levenshtein Distance,允许三种操作:

操作 含义 代价
Insert 插入字符 1
Delete 删除字符 1
Substitute 替换字符 1(相同为 0)

核心逻辑

  • 编辑距离越大,字符串差异越显著;距离为 0 时,字符串完全一致。
  • 与汉明距离的区别:汉明距离仅适用于等长字符串且仅支持替换操作,编辑距离无长度限制、支持三种操作,适用范围更广。
  • 底层实现依赖动态规划,通过构建 DP 表避免暴力枚举的高复杂度(时间复杂度 O ( m × n ) , m / n O(m×n),m/n O(m×n),m/n 为两字符串长度)。

2.算法步骤

2.1 算法步骤讲解

🧠 动态规划的核心思想:"从小问题推大问题"

想象你有一个表格(像 Excel 表格),横着写 B 的每个字母,竖着写 A 的每个字母。

表格里的每一个格子 dp[i][j] 的意思是:

"把 A 的前 i 个字母 → 变成 B 的前 j 个字母,最少要几步?"

我们的任务,就是把这个表格一步步填满,最后右下角那个格子就是答案!

🔑 状态转移方程的通俗解释

现在,我们站在某个格子 dp[i][j] 上,想知道它的值是多少。

这时候,我们看 A 的第 i 个字母 和 B 的第 j 个字母(注意:编程里从 0 开始,所以实际是 A[i-1]B[j-1])。

情况 1️⃣:两个字母一样 (比如都是 't'
  • 那太好了!不需要任何操作。
  • 所以当前的最小步数 = 左上角格子的值(因为前面那部分已经搞定了)。

公式dp[i][j] = dp[i-1][j-1]

情况 2️⃣:两个字母不一样 (比如 'k' vs 's'
  • 这时候我们必须做一次操作 ,但有三种选择,我们要选最省事的那条路
操作 对应哪个格子? 含义
删除 A 的当前字母 dp[i-1][j] 上方 先把 A 的前 i-1 个变成 B 的前 j 个,再把 A 多出的这个字母删掉
插入 B 的当前字母 dp[i][j-1] 左方 先把 A 的前 i 个变成 B 的前 j-1 个,再在末尾加上 B 的这个字母
把 A 的当前字母改成 B 的 dp[i-1][j-1] 左上 先把前面都对齐了,再改这一个字母

👉 这三种方式都要 +1 步 (因为做了一次操作),我们取其中最小的那个

公式dp[i][j] = 1 + min( 上方, 左方, 左上方 )

🖼️ 举个小例子:A="ab", B="abc"

我们填表:

这一列表示A要变空白,需要删除的操作数
"" a b c
只一行表示A要从空变成B,需要插入的操作数 "" 0 1 2 3
a 1 0 1 2
b 2 1 0 1 ← 答案!

看右下角 dp[2][3](即 "ab" → "abc"):

  • A 最后是 'b',B 最后是 'c' → 不一样!
  • 看三个邻居:
    • 上方 dp[1][3] = 2(删 'b',再变 "a"→"abc"?不划算)
    • 左方 dp[2][2] = 0"ab"→"ab" 已完成,再插 'c' → 总共 1 步 ✅)
    • 左上 dp[1][2] = 1"a"→"ab" 花 1 步,再改 'b'→'c' → 共 2 步)

→ 最小的是 左方 + 1 = 0 + 1 = 1 ,所以答案是 1(只需插入 'c')。

✅ 总结一句话:

当前格子的值,取决于"左边、上边、左上角"哪个路径最短,再根据当前两个字母是否相同,决定要不要多花一步。

这就像是在玩一个"文字变形迷宫",每一步都选择代价最小的走法,最终走到终点时,总步数就是编辑距离。

2.2 算法实现伪代码

设字符串 A A A 长度为 m m m, B B B 长度为 n n n。

构建一个 ( m + 1 ) × ( n + 1 ) (m+1)×(n+1) (m+1)×(n+1) 的二维数组 d p dp dp,其中 d p [ i ] [ j ] dp[i][j] dp[i][j] 表示 A [ 0 : i ] A[0:i] A[0:i] 与 B [ 0 : j ] B[0:j] B[0:j] 的编辑距离。

  1. 初始化
    d p [ 0 ] [ j ] = j dp[0][j] = j dp[0][j]=j(空串变 B [ 0 : j ] B[0:j] B[0:j] 需 j j j 次插入)
    d p [ i ] [ 0 ] = i dp[i][0] = i dp[i][0]=i( A [ 0 : i ] A[0:i] A[0:i] 变空串需 i i i 次删除)

  2. 状态转移方程
    对每个 i ∈ [ 1 , m ] , j ∈ [ 1 , n ] i∈[1,m], j∈[1,n] i∈[1,m],j∈[1,n]:

    if A[i-1] == B[j-1]:
    dp[i][j] = dp[i-1][j-1] # 字符相同,无需操作
    else:
    dp[i][j] = 1 + min(
    dp[i-1][j], # 删除 A[i-1]
    dp[i][j-1], # 插入 B[j-1]
    dp[i-1][j-1] # 替换 A[i-1] → B[j-1]
    )

  3. 结果
    d p [ m ] [ n ] dp[m][n] dp[m][n] 即为最终编辑距离。

3.优缺点适用场景

特点 说明
✅ 核心优点 直观易懂 :编辑操作(增、删、改)符合人类直觉。 应用广泛 :是许多文本处理任务的基石算法。 可灵活扩展:可通过为不同操作设置不同代价来适应特定场景。
❌ 主要缺点 计算成本高 :基础动态规划时间复杂度为 O(m*n) ,长文本计算慢。 语义盲区 :只比较字符,不理解单词意义(如无法知道"电脑"和"计算机"是近义词)。 长度敏感:长字符串间的微小差异也会导致距离值较大。
🛠️ 典型使用场景 1. 拼写检查与纠错 :为输入词在词典中快速找到最相似的候选词。 2. 数据清洗 :模糊匹配数据库中的名称(如"Jon Smith"与"John Smith")。 3. 语音识别评估 :计算词错误率(WER),即识别结果与正确文本的编辑距离。 4. 生物信息学 :比较DNA/RNA序列的相似性。 5. NER中 :实体规范化、拼写纠错、边界微调。一般用于 后处理阶段 6. 实体对齐 :不单独使用结合语义网络使用 7. 本体匹配 : 概念名 / 属性名初筛,Tree / Graph Edit Distance(进阶) 8. 搜索 / OCR / 输入纠错:模糊搜索、自动补全、OCR 错误修正

4.主流库推荐

特性 RapidFuzz python-Levenshtein textdistance difflib
速度 ⚡⚡⚡ 最快 ⚡⚡ 快 ⚡ 中等 🐢 慢
编辑距离支持 ✅(需导入子模块) ✅(distance ❌(只有 ratio)
高级模糊匹配 ✅(partial/token/WRatio) 部分 有限
多线程/批量 ✅(process.extract
跨平台安装 ✅(预编译 wheel) ⚠️(偶有编译问题) ✅(标准库)
维护状态 ✅ 活跃 ⚠️ 更新缓慢 ✅ 活跃 ✅(但功能固定)

💡 结论RapidFuzz 是当前 Python 生态中最推荐的模糊匹配库,尤其适合工业级应用。

5.RapidFuzz 使用

1. 极致性能(C++ + SIMD 加速)

  • 底层用 C++ 实现 ,并利用 SIMD 指令集(如 SSE2、AVX2)进行向量化计算;
  • 在大多数 Levenshtein 距离和相似度计算任务中,python-Levenshtein 快 2~10 倍
  • 支持多线程(通过 processes 参数),适合批量处理。

📊 实测参考(10k 字符串对):

  • RapidFuzz: ~0.8 秒
  • python-Levenshtein: ~2.5 秒
  • 纯 Python DP: >30 秒

2. 统一且现代化的 API

  • 所有函数命名清晰,参数一致,支持:

    • 单对字符串比较
    • 一对多(fuzz.ratio, fuzz.partial_ratio
    • 多对多(process.extract, process.cdist
  • 示例:

    python 复制代码
    from rapidfuzz import fuzz, process
    
    # 基础相似度(归一化 Levenshtein)
    score = fuzz.ratio("kitten", "sitting")  # → 57.14...
    
    # 从候选列表中找最匹配项
    choices = ["apple", "appel", "appl", "banana"]
    best = process.extractOne("apple", choices)
    # → ('appel', 88.88..., 1)

3. 丰富的相似度算法

不仅支持 Levenshtein,还内置多种实用变体:

函数 说明
fuzz.ratio 标准归一化编辑距离相似度
fuzz.partial_ratio 子串匹配(适合长短不一的文本)
fuzz.token_sort_ratio 忽略词序(先排序再比)
fzz.token_set_ratio 忽略重复词和顺序(取交集+差集)
fuzz.WRatio 自动选择最优策略(加权混合)

这些对实体对齐、地址/姓名模糊匹配极其有用。

4. 零依赖 & 跨平台友好

  • 纯 C++ 扩展,无需系统级依赖 (不像 python-Levenshtein 有时需 gcc 编译);
  • 提供预编译 wheel,完美支持 Windows、macOS(含 Apple Silicon)、Linux
  • 兼容 Python 3.7+。

5. 活跃维护 & 开源友好


⚠️ 二、潜在缺点或限制

问题 说明
不直接返回编辑距离整数 默认返回归一化相似度 (0~100)。若需原始编辑距离,可用: from rapidfuzz.distance.Levenshtein import distance
内存占用略高(批量时) 多线程/向量化会暂用更多内存,但通常可接受
生态整合稍弱 不如 scikit-learnpandas 原生集成(但可通过 apply 轻松使用)

🔧 获取原始编辑距离示例:

python 复制代码
from rapidfuzz.distance.Levenshtein import distance
d = distance("kitten", "sitting")  # → 3

📦 安装与快速开始

bash 复制代码
pip install rapidfuzz
python 复制代码
from rapidfuzz import fuzz, process
from rapidfuzz.distance.Levenshtein import distance

# 1. 相似度(0-100)
print(fuzz.ratio("hello", "hallo"))  # 80.0

# 2. 原始编辑距离
print(distance("hello", "hallo"))    # 1

# 3. 从列表中找最佳匹配
choices = ["Atlanta Falcons", "New York Jets", "Dallas Cowboys"]
result = process.extractOne("atlanta falcons", choices, scorer=fuzz.WRatio)
print(result)  # ('Atlanta Falcons', 100.0, 0)
相关推荐
小明_GLC10 分钟前
Falcon-TST: A Large-Scale Time Series Foundation Model
论文阅读·人工智能·深度学习·transformer
Python_Study202510 分钟前
制造业数据采集系统选型指南:从技术挑战到架构实践
大数据·网络·数据结构·人工智能·架构
一只大侠的侠14 分钟前
【工业AI热榜】LSTM+GRU融合实战:设备故障预测准确率99.3%,附开源数据集与完整代码
人工智能·gru·lstm
weisian15121 分钟前
入门篇--知名企业-26-华为-2--华为VS阿里:两种科技路径的较量与共生
人工智能·科技·华为·阿里
棒棒的皮皮28 分钟前
【深度学习】YOLO模型精度优化 Checklist
人工智能·深度学习·yolo·计算机视觉
微尘hjx28 分钟前
【数据集 01】家庭室内烟火数据集(按比例划分训练、验证、测试)包含训练好的yolo11/yolov8模型
深度学习·yolov8·yolo11·训练模型·烟火数据集·家庭火灾数据集·火灾数据集
高洁0135 分钟前
CLIP 的双编码器架构是如何优化图文关联的?(2)
python·深度学习·机器学习·知识图谱
线束线缆组件品替网35 分钟前
Bulgin 防水圆形线缆在严苛环境中的工程实践
人工智能·数码相机·自动化·软件工程·智能电视
Cherry的跨界思维41 分钟前
【AI测试全栈:Vue核心】22、从零到一:Vue3+ECharts构建企业级AI测试可视化仪表盘项目实战
vue.js·人工智能·echarts·vue3·ai全栈·测试全栈·ai测试全栈
冬奇Lab42 分钟前
【Cursor进阶实战·07】OpenSpec实战:告别“凭感觉“,用规格驱动AI编程
人工智能·ai编程