惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

P
Proofpoint News Feed
The Last Watchdog
The Last Watchdog
Security Latest
Security Latest
P
Privacy International News Feed
T
Threat Research - Cisco Blogs
H
Help Net Security
T
The Exploit Database - CXSecurity.com
Know Your Adversary
Know Your Adversary
博客园_首页
S
Securelist
S
Schneier on Security
G
GRAHAM CLULEY
Cisco Talos Blog
Cisco Talos Blog
V
Visual Studio Blog
博客园 - 叶小钗
C
Cybersecurity and Infrastructure Security Agency CISA
有赞技术团队
有赞技术团队
Recent Announcements
Recent Announcements
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Microsoft Azure Blog
Microsoft Azure Blog
A
About on SuperTechFans
博客园 - 三生石上(FineUI控件)
Stack Overflow Blog
Stack Overflow Blog
量子位
L
Lohrmann on Cybersecurity
Hugging Face - Blog
Hugging Face - Blog
Engineering at Meta
Engineering at Meta
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
C
CXSECURITY Database RSS Feed - CXSecurity.com
A
Arctic Wolf
P
Privacy & Cybersecurity Law Blog
Simon Willison's Weblog
Simon Willison's Weblog
S
SegmentFault 最新的问题
The Hacker News
The Hacker News
罗磊的独立博客
博客园 - 司徒正美
D
Darknet – Hacking Tools, Hacker News & Cyber Security
博客园 - 【当耐特】
Microsoft Security Blog
Microsoft Security Blog
K
Kaspersky official blog
人人都是产品经理
人人都是产品经理
博客园 - 聂微东
L
LINUX DO - 热门话题
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
V
V2EX
V
Vulnerabilities – Threatpost
AWS News Blog
AWS News Blog
小众软件
小众软件
Project Zero
Project Zero

vivaxy's Blog

SameSite Cookies 解读 - vivaxy's Blog VSCode Conventional Commits 插件 - vivaxy's Blog VSCode Conventional Commits Extension - vivaxy's Blog How Babel Is Built - vivaxy's Blog 一步一步解码 PNG 图片 - vivaxy's Blog Babel 的工程化实现 - vivaxy's Blog 浏览器图像转换手册 - vivaxy's Blog Decoding A PNG Image with JavaScript Step by Step Comprehensive Image Processing on Browsers Use npm Registry to Restric Installing Client Alfred 4 Workflow Open in VSCode Git Tag And Push Git Tag 基于 Custom Elements 的组件化开发 - vivaxy's Blog JavaScript PNG 图片编码和解码 - vivaxy's Blog 如何在多个模块中共享异步数据 - vivaxy's Blog JavaScript 函数式编程初窥 - vivaxy's Blog 用纯 CSS 实现弹窗 - vivaxy's Blog 在浏览器中使用 JavaScript 模块 - vivaxy's Blog Karabiner Elements Hyper 改键设置 - vivaxy's Blog Levenshtein 距离 - vivaxy's Blog
Levenshtein 距离 - vivaxy's Blog
vivaxy · 2017-08-05 · via vivaxy's Blog

简介

Levenshtein 距离是一种编辑距离,用来表示两个字符串的差异。编辑距离是指从字符串 A 开始,修改成字符串 B 的最小步骤数,每个以步骤中,你可以删除一个字符、修改一个字符或者新增一个字符。

比如我们把 acat 变成 gate 的时候,需要做如下的修改:

  • 删除 a
  • 把 c 改成 g
  • 新增 e

所以 acat 和 gate 的 Levenshtein 距离是 3。

算法

这里使用动态规划的方式来实现。

记 Levenshtein 距离为 l(i, j),i 是字符串 A 中从开头到第 i 个下标的子字符串,j 是字符串 B 从开头到第 j 个下标的子字符串,i 和 j 都从 0 开始。

如果 A 中第 i + 1 个字符和 B 中第 j + 1 个字符串相同的话,l(i + 1, j + 1) = min( l(i, j), l(i + 1, j) + 1, l(i, j + 1) + 1)。是把这次添加操作和 l(i + 1, j) 相比是添加了一个字符,和 l(i, j + 1) 相比也是。所以这种情况下只要求得这三种情况下的最小值,就可以得到下一个值了。

相似地,如果 A 中第 i + 1 个字符和 B 中第 j + 1 个字符串不相同的话,l(i + 1, j + 1) = min( l(i, j) + 1, l(i + 1, j) + 1, l(i, j + 1) + 1)。差别是这次操作需要修改一个字符,也就是要在 l(i, j) 上加一步。

所以整理出来的迭代关系是:

cost = 1 if stringA[i] == stringB[j] else 0
l(i + 1, j + 1) = min( l(i, j) + cost, l(i + 1, j) + 1, l(i, j + 1) + 1 )

再看下边界值:

l(0, 0) = 0
l(i, 0) = i
l(0, j) = j

字符串长度都为 0 时,那么 Levenshtein 距离是 0。当其中一个字符串长度为 0 时,Levenshtein 距离是另一个字符串的长度。这很容易理解。

所以最后代码的实现(这里用的是 python):

class Levenshtein(object):
    def __init__(self, stringA, stringB):
        self.stringA = stringA
        self.stringB = stringB
        matrix = [list(range(0, len(stringA) + 1))]
        for i in range(len(stringB)):
            row = [i + 1]
            for j in range(len(stringA)):
                value = min(matrix[i][j + 1] + 1, row[j] + 1)
                cost = 0 if stringB[i] == stringA[j] else 1
                value = min(value, matrix[i][j] + cost)
                row.append(value)
            matrix.append(row)
        self.matrix = matrix

    def getDistance(self):
        return self.matrix[len(self.stringB)][len(self.stringA)]

我们把 l(i, j) 记录在矩阵中,那么最后的结果就是 矩阵右下角的值。

然后加上测试用例:

import unittest

class Test(unittest.TestCase):
    def test(self):
        levenshtein = Levenshtein('kitten', 'sitting')
        self.assertEqual(levenshtein.getDistance(), 3)
        levenshtein = Levenshtein('GUMBO', 'GAMBOL')
        self.assertEqual(levenshtein.getDistance(), 2)
        levenshtein = Levenshtein('', 'abcde')
        self.assertEqual(levenshtein.getDistance(), 5)
        levenshtein = Levenshtein('abcdef', '')
        self.assertEqual(levenshtein.getDistance(), 6)
        levenshtein = Levenshtein('acat', 'gate')
        self.assertEqual(levenshtein.getDistance(), 3)


if __name__ == '__main__':
    unittest.main()

查看完整代码