大家好,我是涛哥。
又是周末,过去的几天工作日,可能你会忙得焦头乱额,但毕竟是充实的,忙碌才是生活和工作的常态,也值得欣慰。
今天,我们来聊一个经典的面试题:编辑距离。无论是面试字节跳动、腾讯还是阿里,编辑距离必须掌握,且要熟练写出代码。
涛哥手绘(老虎与猫的编辑距离)
一. 编辑距离简介
编辑距离,可以用于度量两个字符串的差异。其含义是:一个字符串到另一个字符串的最少变换次数,其中,变换操作仅涉及增删改,且每次只能操作一个字符。
编辑距离的应用很广,比如常见的纠错检错原理:当你在浏览器输入错误单词ggood后,通过编辑距离匹配,能自动给你提示出正确的单词good, 挺有用的吧?
二. 编辑距离题目
编辑距离,在笔试面试中会经常涉及到。一个朋友最近参加了字节跳动公司的面试,便遇到了编辑距离,题目如下:
实话实话,如果没有提前准备,那么现场是很难想出解题思路的,也就别谈通过面试了。有些东西,必须提前准备,知己知彼,方能百战不殆。
三. 编辑距离算法
编辑距离,是一个典型的动态规划问题。那么,从一个字符串到另一个字符串的编辑距离一定存在吗?这是必然的,暴力方法可以证明,不必赘述。
记dp[i][j]为字符串A的前i个字符到字符串B的前j个字符的编辑距离,如果A或B为空串,那么i或j为0,因此i的区间为[0, lenA], j的区间为[0, lenB].
接下来,我们来推导动态规划的状态转移方程。
(1) 如果A[i]等于B[j], 那么此时便是躺赢的模式,即有:
dp[i][j] = dp[i-1][j-1]
(2) 如果A[i]不等于B[j], 则可以进行三种操作(增删改),如下:
-
把A的前i-1个字符变成B的前j个字符,然后删除一个字符;
-
把A的前i个字符变成B的前j-1个字符,然后增加一个字符;
-
把A的前i-1个字符变成B的前j-1个字符,然后改变一个字符;
显然,此时的动态规划方程为:
dp[i][j] = 1 +
min{dp[i-1][j],dp[i][j-1],dp[i-1][j-1]}
四. 编辑距离编程
算法搞懂后,程序就简单了,我们以golang代码为例来编程:
func minDistance(A string, B string) int {
lenA := len(A)
lenB := len(B)
// 初始化二维slice
dp := make([][]int, lenA + 1)
for i := 0; i < lenA + 1; i++ {
dp[i] = make([]int, lenB + 1)
}
for i := 0; i < lenA + 1; i++ {
dp[i][0] = i
}
for j := 0; j < lenB + 1; j++ {
dp[0][j] = j
}
for i := 1; i < lenA + 1; i++ {
for j := 1; j < lenB + 1; j++ {
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], dp[i][j-1], dp[i-1][j-1])
}
}
}
return dp[lenA][lenB]
}
func min(x, y, z int) int {
m := x
if x > y {
m = y
}
if m > z {
m = z
}
return m
}
经自测,程序通过,结果无误。从算法到代码实现,都是OK的,能通过面试。
到此为止,我们对编辑距离的求法有了彻底的掌握,也顺便和大家一起复习了动态规划的内容。
希望大家在面试前要专门地准备,既然大厂爱考这些,又提供那么高的工资,那就好好准备吧。