最短编辑距离问题与动态规划----LeetCode 72.编辑距离

动态规划(Dynamic Programming, DP)是解决复杂问题的一个强大工具,它将问题分解成更小的子问题,并使用这些子问题的解决方案来构建整体问题的解决方案。在深入探讨最短编辑距离问题之前,让我们先理解什么是动态规划,以及如何通过动态规划的视角来看待这个问题。

原题链接:72. 编辑距离 - 力扣(LeetCode)

动态规划分析

动态规划的核心

动态规划通常用于求解最优化问题。其核心思想包括两个主要部分:

  1. **最优子结构:**问题的最优解包含其子问题的最优解。这意味着我们可以通过合并子问题的最优解来构造整个问题的最优解。

  2. **重叠子问题:**在解决问题的过程中,问题被分解成若干个子问题,其中很多子问题是重复的。

最短编辑距离的动态规划解法

在最短编辑距离问题中,我们要将一个字符串word1转换成另一个字符串word2,并且我们希望所需的操作次数尽可能少。这里的操作包括插入、删除和替换字符。

集合定义

在这个问题中,我们定义f[i][j]word1的前i个字符转换到word2的前j个字符所需的最少操作次数。这个定义本身就隐含了一个最优子结构的性质,即要得到f[i][j]的值,我们可以依赖于f[i-1][j]f[i][j-1]f[i-1][j-1]的值,这些都是更小的子问题。

属性

在这个场景下,我们关注的属性是最小值(min),因为我们要找的是最少的操作次数。

转移方程

为了构建f[i][j],我们考虑以下三种可能的最后一步操作:

  • 插入: 我们可以先将word1的前i个字符转换为word2的前j-1个字符,然后在末尾插入word2的第j个字符。这给我们 f[i][j-1] + 1

  • 删除: 我们可以先将word1的前i-1个字符转换为word2的前j个字符,然后删除word1的第i个字符。这给我们 f[i-1][j] + 1

  • ++替换或保持++: 如果word1[i]word2[j]相同,我们不需要任何操作,只需要保持即可。如果它们不同,我们需要将word1[i]替换为word2[j],这给我们 f[i-1][j-1] + 1(如果不同)或**f[i-1][j-1]**(如果相同)。

综上所述,转移方程可以表示为:

java 复制代码
f[i][j] = min(      f[i][j - 1] + 1, 
                    f[i - 1][j] + 1, 
                    f[i - 1][j - 1] + (word1[i] != word2[j])
          );

其中,(word1[i] != word2[j]) 是一个指示函数,word1[i]不等于word2[j]时值为1,否则为0。

实现

基于上述分析,我们可以实现动态规划解法来解决最短编辑距离问题。

java 复制代码
class Solution {
    public int minDistance(String word1, String word2) {
        int n = word1.length();
        int m = word2.length();
        int[][] f = new int[505][505];

        
        // 有一个字符串为空串
        if (n * m == 0) {
            return n + m;
        }

        for (int i = 1; i <= n; i++ ) {
            f[i][0] = i;
        }

        for (int i = 1; i <= m; i++ ) {
            f[0][i] = i;
        }

        for (int i = 1; i <= n; i++ ) {
            for (int j = 1; j <= m; j++ ) {
                f[i][j] = Math.min(f[i - 1][j] + 1, f[i][j - 1] + 1);
                f[i][j] = Math.min(f[i][j], f[i - 1][j - 1] + (word1.charAt(i - 1) == word2.charAt(j - 1) ? 0 : 1));
            }
        }
        return f[n][m];
    }
}
java 复制代码
import java.util.*;

public class Main{
    public static void main(String[] args) {
        Scanner sca = new Scanner(System.in);
        int N = 1010; // 假设字符串的最大长度
        char[] a = new char[N];
        char[] b = new char[N];
        int[][] f = new int[N][N]; // 动态规划数组

        int n = sca.nextInt(); // word1的长度
        String A = sca.next(); // word1
        int m = sca.nextInt(); // word2的长度
        String B = sca.next(); // word2

        // 初始化边界条件
        for (int i = 1; i <= n; i++ ) {
            a[i] = A.charAt(i - 1);
            f[i][0] = i;
        }
        for (int i = 1; i <= m; i++ ) {
            b[i] = B.charAt(i - 1);
            f[0][i] = i;
        }

        // 动态规划填表
        for (int i = 1; i <= n; i++ ) {
            for (int j = 1; j <= m; j++ ) {
                f[i][j] = Math.min(f[i][j - 1] + 1, f[i - 1][j] + 1);
                if (a[i] == b[j]) f[i][j] = Math.min(f[i][j], f[i - 1][j - 1]);
                else f[i][j] = Math.min(f[i][j], f[i - 1][j - 1] + 1);
            }
        }

        System.out.println(f[n][m]); // 输出结果
    }
}

时间复杂度

这个解法的时间复杂度为O(nm),其中nm分别是字符串word1word2的长度。这是因为我们需要填充一个n x m的二维数组。

相关推荐
敲代码中2 分钟前
Maven入门到精通
java·maven
姜不吃葱7 分钟前
【力扣热题100】双指针—— 接雨水
数据结构·算法·leetcode·力扣热题100
PineappleCoder12 分钟前
大小写 + 标点全搞定!JS 如何精准统计单词频率?
前端·javascript·算法
zzx_blog21 分钟前
简单易懂的leetcode 100题-第三篇 移动0,颜色分类,数组中的第K个最大元素
leetcode·面试
拂晓银砾23 分钟前
Java数据结构-队列
java·数据结构
重生成为编程大王24 分钟前
Java ConcurrentHashMap 深度解析
java·开发语言
John.Lewis25 分钟前
数据结构初阶(19)外排序·文件归并排序的实现
c语言·数据结构·排序算法
John.Lewis29 分钟前
数据结构初阶(16)排序算法——归并排序
c语言·数据结构·排序算法
阿华的代码王国31 分钟前
【Android】适配器与外部事件的交互
android·xml·java·前端·后端·交互
MacroZheng41 分钟前
还在用WebSocket实现即时通讯?试试MQTT吧,真香!
java·spring boot·后端