一、理解
1.1 场景
当百度搜索错误时,下方会有提示,你要找的是不是xxx?
这样的一个功能是怎么实现的呢?
从最简单的思路出发,维护一套关键词词库,当搜索时,去与词库比较,哪一个最相似即可。
关键就在于最相似,如何实现?这就需要编辑算法了。
每步只能执行如下3个操作中的一个,
插入一个字符
删除一个字符
替换一个字符
将如图的word1换成word2最少需要几步。这就是编辑算法。
1.2 递归解法
求word1转换成word2最少步骤的基本思路
- 若word1[m]==word2[n],则继续比较word1[0..m-1]与word[0..n-1]的结果。
- 若word1[m]!=word2[n],则选择下面三个选项中最小的那个加1(表示进行了一次替换)
- 对word1尾部执行插入相同字符操作,work1与word2尾部抵消掉,比较word1[0..m]与word2[0..n-1]
- 对word1尾部执行删除字符操作,比较word1[0..m-1]与word2[0..n]
- 对word1尾部执行替换成word2相同字符操作,抵消掉,比较word1[0..m-1]与word[0..n-1]
下面是递归实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
public static int recursion(String a, String b) { if (a.length() * b.length() == 0) { return a.length() + b.length(); } if (a.substring(a.length() - 1).equals(b.substring(b.length() - 1))) { return recursion(a.substring(0, a.length() - 1), b.substring(0, b.length() - 1)); } else { int insert = recursion(a, b.substring(0, b.length() - 1)); int delete = recursion(a.substring(0, a.length() - 1), b); int replace = recursion(a.substring(0, a.length() - 1), b.substring(0, b.length() - 1)); return Math.min(Math.min(insert, delete), replace) + 1; } }
|
需要理解,一旦涉及子问题,可以使用自顶向下的递归,或者,自底向上的动态规划。
1.3 动态规划解法
想要使用动态规划,先要抽象出状态转移方程。
word1与word2是两个对应的状态,所以一定是二维状态数组。
抽象出状态转移方程
- 如果word1[i]==word2[j],那么dp[i][j]=dp[i-1][j-1]
- 否则,dp[i][j]=1+min(dp[i][j-1], dp[i-1][j], dp[i-1][j-1])
dp表示dynamicProgramming
构建二维数组如下
动态规划实现如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
|
public static int dynamicProgramming(String a, String b) { int m = a.length(); int n = b.length(); if (m * n == 0) { return m + n; } int[][] d = new int[n + 1][m + 1]; for (int i = 0; i < m + 1; i++) { d[0][i] = i; } for (int j = 0; j < n + 1; j++) { d[j][0] = j; } for (int i = 1; i < n + 1; i++) { for (int j = 1; j < m + 1; j++) { int replace = d[i - 1][j - 1]; int insert = d[i][j - 1]; int delete = d[i - 1][j]; if (a.charAt(j - 1) == b.charAt(i - 1)) { d[i][j] = replace; } else { d[i][j] = Math.min(Math.min(insert, delete), replace) + 1; } } } return d[n][m]; }
|
1.4 效率分析
假设两个转换的串,最长的一个串长度为n。
从最坏的角度出发,递归时间复杂度为\(3^n\),动态规划时间复杂度\(n^2\)
当n越大时,动态规划的优势就越明显。
二、参考
72. 编辑距离 - 力扣(LeetCode)
java - charAt() 还是子字符串? 哪个更快? - 堆栈溢出