package main
import (
"fmt"
)
func main() {
var s, t string
fmt.Scanln(&s)
fmt.Scanln(&t)
result := editDistance(s, t)
fmt.Println(result)
}
// editDistance 计算两个字符串的编辑距离
// 使用动态规划算法,时间复杂度O(mn),空间复杂度O(mn)
func editDistance(s, t string) int {
m, n := len(s), len(t)
// dp[i][j] 表示 s[0:i] 和 t[0:j] 的编辑距离
dp := make([][]int, m+1)
for i := range dp {
dp[i] = make([]int, n+1)
}
// 初始化边界条件
// s为空字符串时,需要插入t的所有字符
for j := 0; j <= n; j++ {
dp[0][j] = j
}
// t为空字符串时,需要删除s的所有字符
for i := 0; i <= m; i++ {
dp[i][0] = i
}
// 动态规划填表
for i := 1; i <= m; i++ {
for j := 1; j <= n; j++ {
if s[i-1] == t[j-1] {
// 字符相同,不需要操作
dp[i][j] = dp[i-1][j-1]
} else {
// 字符不同,取三种操作的最小值
dp[i][j] = min(
dp[i-1][j]+1, // 删除s[i-1]
dp[i][j-1]+1, // 插入t[j-1]
dp[i-1][j-1]+1, // 替换s[i-1]为t[j-1]
)
}
}
}
return dp[m][n]
}
// 空间优化版本:只使用O(min(m,n))空间
func editDistanceOptimized(s, t string) int {
m, n := len(s), len(t)
// 确保s是较短的字符串,优化空间使用
if m > n {
s, t = t, s
m, n = n, m
}
// 只需要两行:当前行和上一行
prev := make([]int, m+1)
curr := make([]int, m+1)
// 初始化第一行
for i := 0; i <= m; i++ {
prev[i] = i
}
// 逐行计算
for j := 1; j <= n; j++ {
curr[0] = j // 边界条件
for i := 1; i <= m; i++ {
if s[i-1] == t[j-1] {
curr[i] = prev[i-1]
} else {
curr[i] = min(
prev[i]+1, // 删除
curr[i-1]+1, // 插入
prev[i-1]+1, // 替换
)
}
}
// 交换prev和curr
prev, curr = curr, prev
}
return prev[m]
}
// 递归+记忆化版本(展示不同实现思路)
func editDistanceMemo(s, t string) int {
m, n := len(s), len(t)
memo := make([][]int, m+1)
for i := range memo {
memo[i] = make([]int, n+1)
for j := range memo[i] {
memo[i][j] = -1
}
}
var dfs func(i, j int) int
dfs = func(i, j int) int {
// 边界条件
if i == 0 {
return j
}
if j == 0 {
return i
}
// 记忆化
if memo[i][j] != -1 {
return memo[i][j]
}
if s[i-1] == t[j-1] {
memo[i][j] = dfs(i-1, j-1)
} else {
memo[i][j] = min(
dfs(i-1, j)+1, // 删除
dfs(i, j-1)+1, // 插入
dfs(i-1, j-1)+1, // 替换
)
}
return memo[i][j]
}
return dfs(m, n)
}
// min 返回三个数中的最小值
func min(a, b, c int) int {
if a <= b && a <= c {
return a
}
if b <= c {
return b
}
return c
}
// 用于两个数的min函数
func min2(a, b int) int {
if a < b {
return a
}
return b
}
解题思路
算法分析
这道题是经典的动态规划问题——编辑距离(Levenshtein距离)。主要涉及:
- 状态定义:dp[i][j]表示s[0:i]和t[0:j]的编辑距离
- 状态转移:根据字符是否相同选择最优操作
- 边界处理:空字符串的初始化
- 空间优化:滚动数组优化空间复杂度
动态规划状态转移
graph TD
A[dp[i][j]] --> B{s[i-1] == t[j-1]?}
B -->|是| C[dp[i-1][j-1]]
B -->|否| D[三种操作取最小值]
D --> E[删除: dp[i-1][j] + 1]
D --> F[插入: dp[i][j-1] + 1]
D --> G[替换: dp[i-1][j-1] + 1]
E --> H[min三者]
F --> H
G --> H
状态转移方程
graph LR
A[状态转移方程] --> B[dp[i][j]]
B --> C{字符是否相同}
C -->|相同| D[dp[i-1][j-1]]
C -->|不同| E[min操作]
E --> F[删除s[i-1]]
E --> G[插入t[j-1]]
E --> H[替换为t[j-1]]
算法流程图
flowchart TD
A[读取字符串s和t] --> B[获取长度m=len(s), n=len(t)]
B --> C[创建dp数组 (m+1)×(n+1)]
C --> D[初始化边界条件]
D --> E[dp[0][j] = j for all j]
E --> F[dp[i][0] = i for all i]
F --> G[双重循环填表]
G --> H{s[i-1] == t[j-1]?}
H -->|是| I[dp[i][j] = dp[i-1][j-1]]
H -->|否| J[dp[i][j] = min三种操作]
I --> K{循环结束?}
J --> K
K -->|否| G
K -->|是| L[返回dp[m][n]]
DP表格示例
以s="abc", t="ab"为例:
graph TD
A[DP表格构建过程]
A --> B[
" ε a b
ε 0 1 2
a 1 0 1
b 2 1 0
c 3 2 1"
]
B --> C[最终答案: dp[3][2] = 1]
三种操作详解
graph TD
A[编辑操作] --> B[插入操作]
A --> C[删除操作]
A --> D[替换操作]
B --> E[在s中插入字符]
B --> F[代价: dp[i][j-1] + 1]
C --> G[从s中删除字符]
C --> H[代价: dp[i-1][j] + 1]
D --> I[替换s中字符]
D --> J[代价: dp[i-1][j-1] + 1]
代码实现思路
- 基础DP版本:二维数组存储所有状态时间复杂度O(mn),空间复杂度O(mn)易于理解和调试
- 空间优化版本:滚动数组优化空间时间复杂度O(mn),空间复杂度O(min(m,n))适用于大规模数据
- 记忆化递归版本:自顶向下的思考方式代码更直观,容易理解递归关系适合面试讲解
时间复杂度分析
基础DP | O(mn) | O(mn) | 经典实现 |
空间优化 | O(mn) | O(min(m,n)) | 节省空间 |
记忆化递归 | O(mn) | O(mn) | 易于理解 |
关键优化技巧
- 空间优化:
- 字符串长度优化:
- 边界条件处理:
实际应用场景
- 文本相似度计算:搜索引擎的模糊匹配
- 拼写检查:单词纠错功能
- DNA序列比对:生物信息学中的序列分析
- 版本控制:Git中的文件差异比较
- 自然语言处理:文本相似度度量
算法扩展
graph TD
A[编辑距离扩展] --> B[加权编辑距离]
A --> C[最长公共子序列]
A --> D[最长公共子串]
A --> E[字符串匹配]
B --> F[不同操作不同代价]
C --> G[只允许插入删除]
D --> H[连续字符匹配]
E --> I[模式匹配算法]
面试考点
- 状态定义能力:正确定义DP状态
- 状态转移推导:理解三种操作的含义
- 边界条件处理:空字符串的初始化
- 空间优化思维:滚动数组的使用
- 实际应用理解:算法的实际价值
测试用例分析
graph TD
A[测试用例分析] --> B[基础用例: abcdefg vs abcdef]
A --> C[相同字符串: abc vs abc]
A --> D[完全不同: abc vs xyz]
A --> E[空字符串: "" vs abc]
B --> F[结果: 1,删除g]
C --> G[结果: 0,无需操作]
D --> H[结果: 3,全部替换]
E --> I[结果: 3,全部插入]
这个问题是动态规划的经典应用,展示了如何将复杂问题分解为子问题,并通过状态转移方程求解最优解。编辑距离算法在实际工程中有广泛应用,是面试中的高频考点。

京公网安备 11010502036488号