|
| 1 | +package com.mootal.algo.day9_72; |
| 2 | + |
| 3 | +/** |
| 4 | + * Hard |
| 5 | + * (注解文档查看快捷键 选中类名或方法名 按ctrl + Q) |
| 6 | + * <p> |
| 7 | + * 思维全过程记录方案:<p> |
| 8 | + * 1 背基础结构和算法 | 记录在课程笔记<p> |
| 9 | + * 2 看题 -> 悟题 思考过程 | 记录在wiki<p> |
| 10 | + * 3 悟题 -> 写题 实现难点 | 记录在代码注解<p> |
| 11 | + * 4 写题 -> 优化 多种解法 | 记录在leetcode提交 |
| 12 | + * <p> |
| 13 | + * 问题: |
| 14 | + * Given two words word1 and word2, |
| 15 | + * find the minimum number of operations required to convert word1 to word2. |
| 16 | + * <p> |
| 17 | + * 题解方案topics: |
| 18 | + * string、dp |
| 19 | + * |
| 20 | + * @author li tong |
| 21 | + * @date 2019/6/11 10:06 |
| 22 | + * @see Object |
| 23 | + * @since 1.0 |
| 24 | + */ |
| 25 | +public class EditDistance72 { |
| 26 | + |
| 27 | + public static void main(String[] args) { |
| 28 | +// String word1 = "abc", word2 = "abc"; |
| 29 | +// String word1 = "horse", word2 = "rose"; |
| 30 | + String word1 = "rose", word2 = "horse"; |
| 31 | + System.out.println(minDistance(word1, word2)); |
| 32 | + System.out.println(minDistanceDP(word1, word2)); |
| 33 | + } |
| 34 | + |
| 35 | + /** |
| 36 | + * 递归解法 <p> |
| 37 | + * 自己思考结合题解提示,主干思路:<p> |
| 38 | + * 1.先过一遍所有可能的解决方案 找到递归思路 2 用mem优化<p> |
| 39 | + * 自己学习 思考 实践时的难点(难点是如何突破的见wiki):<p> |
| 40 | + * 1 看题解思路 自己尝试实现代码 写出相似度50% 再看答案 |
| 41 | + * 2 边界条件处理 |
| 42 | + * 3 多种路径组合出正确的递归写法 |
| 43 | + * 4 如何合并到最终结果<p> |
| 44 | + * |
| 45 | + * @param word1 |
| 46 | + * @param word2 |
| 47 | + * @return |
| 48 | + */ |
| 49 | + public static int minDistance(String word1, String word2) { |
| 50 | + char[] w1 = word1.toCharArray(); |
| 51 | + char[] w2 = word2.toCharArray(); |
| 52 | + int l1 = w1.length, l2 = w2.length; |
| 53 | + int[][] mem = new int[l1][l2]; |
| 54 | +// return help(0, w1, 0, w2, mem); |
| 55 | + return helpWithMem(0, w1, 0, w2, mem); |
| 56 | + } |
| 57 | + |
| 58 | + private static int help(int i, char[] w1, int j, char[] w2, int[][] mem) { |
| 59 | + int insertCnt, deleteCnt, replaceCnt; |
| 60 | + if (i == w1.length) { |
| 61 | + // 长短不一致的情况 例如 rose -> horse |
| 62 | + // 这里自己实现起来很困难 很难想到 |
| 63 | + return w2.length - j; |
| 64 | + } |
| 65 | + if (j == w2.length) { |
| 66 | + // 长短不一致的情况 例如 horse -> rose |
| 67 | + // 这里自己实现起来很困难 很难想到 |
| 68 | + return w1.length - i; |
| 69 | + } |
| 70 | + int res = 0; |
| 71 | + if (w1[i] == w2[j]) { |
| 72 | + // 直接return |
| 73 | +// System.out.println("i=" + i); |
| 74 | + res = help(i + 1, w1, j + 1, w2, mem); |
| 75 | +// System.out.println("i=" + i); |
| 76 | + return res; |
| 77 | + } else { |
| 78 | + deleteCnt = help(i + 1, w1, j, w2, mem); |
| 79 | + System.out.println(deleteCnt); |
| 80 | + insertCnt = help(i, w1, j + 1, w2, mem); |
| 81 | + System.out.println(insertCnt); |
| 82 | + replaceCnt = help(i + 1, w1, j + 1, w2, mem); |
| 83 | + System.out.println(replaceCnt); |
| 84 | + // 这里很难想到是取三者的最小值,还要加1是什么意思 |
| 85 | + res = Math.min(insertCnt, Math.min(deleteCnt, replaceCnt)) + 1; |
| 86 | + // 自己写的代码 意思接近了 |
| 87 | +// if (w1[i + 1] == w2[j]) { |
| 88 | +// i = i + 1; |
| 89 | +// help(i + 1, w1, j, w2); |
| 90 | +// deleteCnt++; |
| 91 | +// } else if (w1[i] == w2[j + 1]) { |
| 92 | +// j = j + 1; |
| 93 | +// help(i, w1, j, w2); |
| 94 | +// insertCnt++; |
| 95 | +// } else { |
| 96 | +// i = i + 1; |
| 97 | +// j = j + 1; |
| 98 | +// help(i, w1, j, w2); |
| 99 | +// replaceCnt++; |
| 100 | +// } |
| 101 | + } |
| 102 | + return res; |
| 103 | + } |
| 104 | + |
| 105 | + private static int helpWithMem(int i, char[] w1, int j, char[] w2, int[][] mem) { |
| 106 | + int insertCnt, deleteCnt, replaceCnt; |
| 107 | + if (i == w1.length) { |
| 108 | + return w2.length - j; |
| 109 | + } |
| 110 | + if (j == w2.length) { |
| 111 | + return w1.length - i; |
| 112 | + } |
| 113 | + if (mem[i][j] > 0) { |
| 114 | + return mem[i][j]; |
| 115 | + } |
| 116 | + int res = 0; |
| 117 | + if (w1[i] == w2[j]) { |
| 118 | +// System.out.println("i=" + i); |
| 119 | + res = helpWithMem(i + 1, w1, j + 1, w2, mem); |
| 120 | +// System.out.println("i=" + i); |
| 121 | + return res; |
| 122 | + } else { |
| 123 | + deleteCnt = helpWithMem(i + 1, w1, j, w2, mem); |
| 124 | +// System.out.println(deleteCnt); |
| 125 | + insertCnt = helpWithMem(i, w1, j + 1, w2, mem); |
| 126 | +// System.out.println(insertCnt); |
| 127 | + replaceCnt = helpWithMem(i + 1, w1, j + 1, w2, mem); |
| 128 | +// System.out.println(replaceCnt); |
| 129 | + mem[i][j] = Math.min(insertCnt, Math.min(deleteCnt, replaceCnt)) + 1; |
| 130 | + } |
| 131 | + return mem[i][j]; |
| 132 | + } |
| 133 | + |
| 134 | + /** |
| 135 | + * DP解法 <p> |
| 136 | + * 看题解,主干思路:<p> |
| 137 | + * dp[i][j] 表示从 word1 的前i个字符转换到 word2 的前j个字符所需要的步骤<p> |
| 138 | + * 自己学习 思考 实践时的难点(难点是如何突破的见wiki):<p> |
| 139 | + * 找出状态转移方程 思路:手写例子 简化变量 |
| 140 | + * DP特点 |
| 141 | + * 1 求极值 |
| 142 | + * 2 子问题最优解 |
| 143 | + * 3 dp和递归不同的是 dp是循环 递归是栈 |
| 144 | + * 4 dp倾向于倒序<p> |
| 145 | + * . Ø a b c d<p> |
| 146 | + * Ø 0 1 2 3 4<p> |
| 147 | + * b 1 1 1 2 3<p> |
| 148 | + * b 2 2 1 2 3<p> |
| 149 | + * c 3 3 2 1 2<p> |
| 150 | + * |
| 151 | + * @param word1 |
| 152 | + * @param word2 |
| 153 | + * @return |
| 154 | + */ |
| 155 | + public static int minDistanceDP(String word1, String word2) { |
| 156 | + char[] w1 = word1.toCharArray(); |
| 157 | + char[] w2 = word2.toCharArray(); |
| 158 | + int l1 = w1.length, l2 = w2.length; |
| 159 | + int[][] dp = new int[l1 + 1][l2 + 1]; |
| 160 | + for (int i = 0; i <= l1; ++i) { |
| 161 | + dp[i][0] = i; |
| 162 | + } |
| 163 | + for (int i = 0; i <= l2; ++i) { |
| 164 | + dp[0][i] = i; |
| 165 | + } |
| 166 | + for (int i = 1; i <= l1; ++i) { |
| 167 | + for (int j = 1; j <= l2; ++j) { |
| 168 | + if (w1[i - 1] == w2[j - 1]) { |
| 169 | + dp[i][j] = dp[i - 1][j - 1]; |
| 170 | + } else { |
| 171 | + dp[i][j] = Math.min(dp[i - 1][j - 1], Math.min(dp[i - 1][j], dp[i][j - 1])) + 1; |
| 172 | + } |
| 173 | + } |
| 174 | + } |
| 175 | + return dp[l1][l2]; |
| 176 | + } |
| 177 | + |
| 178 | +} |
0 commit comments