From d951e56db780335ccda33b65fd057d0cd1d4b669 Mon Sep 17 00:00:00 2001 From: markilue <745518019@qq.com> Date: Tue, 20 Dec 2022 12:46:24 +0800 Subject: [PATCH] =?UTF-8?q?leecode=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../leecode/dynamic/T35_MinDistance.java | 142 ++++++++++++++++++ .../leecode/dynamic/T36_MinDistance.java | 132 ++++++++++++++++ 2 files changed, 274 insertions(+) create mode 100644 Leecode/src/main/java/com/markilue/leecode/dynamic/T35_MinDistance.java create mode 100644 Leecode/src/main/java/com/markilue/leecode/dynamic/T36_MinDistance.java diff --git a/Leecode/src/main/java/com/markilue/leecode/dynamic/T35_MinDistance.java b/Leecode/src/main/java/com/markilue/leecode/dynamic/T35_MinDistance.java new file mode 100644 index 0000000..e53c75c --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/dynamic/T35_MinDistance.java @@ -0,0 +1,142 @@ +package com.markilue.leecode.dynamic; + + +import org.junit.Test; + +/** + *@BelongsProject: Leecode + *@BelongsPackage: com.markilue.leecode.dynamic + *@Author: dingjiawen + *@CreateTime: 2022-12-20 10:17 + *@Description: + * TODO 力扣583题 两个字符串的删除操作: + * 给定两个单词 word1 和 word2 ,返回使得 word1 和 word2 相同所需的最小步数。 + * 每步 可以删除任意一个字符串中的一个字符。 + *@Version: 1.0 + */ +public class T35_MinDistance { + + @Test + public void test(){ + String word1 = "sea"; + String word2 = "eat"; + System.out.println(minDistance1(word1,word2)); + } + + @Test + public void test1(){ + String word1 = "leetcode"; + String word2 = "etco"; + System.out.println(minDistance1(word1,word2)); + } + + /** + * 思路:本质上还是求最少删除多少个字符会相等 + * TODO 动态规划法: + * (1)dp定义:dp[i][j]表示使用word1[0-i]和word2[0-j]需要删除多少个变成一样 + * (2)dp状态转移方程: + * 1.如果两个数相等:和以前没加这两个数时一样 + * if word1[i]==word2[j] dp[i][j]=dp[i-1][j-1] + * 2.如果两个数不相等:可以去掉当前这个word1[i]或者去掉word1[j]看谁减去的字符多 + * else dp[i][j]=min(dp[i-1][j]+1,dp[i][j-1]+1) + * (3)dp初始化:dp[i][0]=i;dp[0][i]=i + * (4)dp遍历顺序: + * (5)dp举例推导:以word1 = "sea", word2 = "eat"为例 + * [0 e a t] + * i=0: 0 1 2 3 + * i=s: 1 2 3 4 + * i=e: 2 1 2 3 + * i=a: 3 2 1 2 + * 速度击败62.85%,内存击败66.73% + * @param word1 + * @param word2 + * @return + */ + public int minDistance(String word1, String word2) { + + int[][] dp = new int[word1.length() + 1][word2.length() + 1]; + //初始化 + for (int i = 0; i < dp.length; i++) { + dp[i][0]=i; + } + for (int i = 1; i < dp[0].length; i++) { + dp[0][i]=i; + } + + for (int i = 1; i < dp.length; i++) { + for (int j = 1; j < dp[0].length; j++) { + if (word1.charAt(i-1)==word2.charAt(j-1)) dp[i][j]=dp[i-1][j-1]; + else dp[i][j]=Math.min(dp[i-1][j]+1,dp[i][j-1]+1); + } + } + + return dp[word1.length()][word2.length()]; + + } + + + /** + * 代码随想录另一种思路:这题和求最长公共子序列类似: + * 只要求出两个字符串的最长公共子序列长度即可,那么除了最长公共子序列之外的字符都是必须删除的, + * 最后用两个字符串的总长度减去两个最长公共子序列的长度就是删除的最少步数。 + * 速度击败62.85%,内存击败14.5% + * @param word1 + * @param word2 + * @return + */ + public int minDistance1(String word1, String word2) { + + int[][] dp = new int[word1.length() + 1][word2.length() + 1]; + //初始化 + for (int i = 1; i < dp.length; i++) { + for (int j = 1; j < dp[0].length; j++) { + if (word1.charAt(i-1)==word2.charAt(j-1)) dp[i][j]=dp[i-1][j-1]+1; + else dp[i][j]=Math.max(dp[i-1][j],dp[i][j-1]); + } + } + + return word1.length()+word2.length()-dp[word1.length()][word2.length()]*2; + + } + + /** + * 官方题解中最快:本质上是最长子序列的解法 + * 速度击败100%,内存击败91.88% 3ms + * @param word1 + * @param word2 + * @return + */ + public int minDistance2(String word1, String word2) { + int m = word1.length(); + int n = word2.length(); + if (m < n) { + String s = word1; + word1 = word2; + word2 = s; + + int t = m; + m = n; + n = t; + } + char[] w1 = word1.toCharArray(); + char[] w2 = word2.toCharArray(); + int[] prev = new int[n + 1]; + int[] curr = new int[n + 1]; + for (char c : w1) { + for (int j = 0; j < n; j++) { + if (c == w2[j]) { + curr[j + 1] = prev[j] + 1; + } else { + curr[j + 1] = Math.max(prev[j + 1], curr[j]); + } + } + int[] t = prev; + prev = curr; + curr = t; + } + return m + n - 2 * prev[n]; + } + + + +} diff --git a/Leecode/src/main/java/com/markilue/leecode/dynamic/T36_MinDistance.java b/Leecode/src/main/java/com/markilue/leecode/dynamic/T36_MinDistance.java new file mode 100644 index 0000000..f635db2 --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/dynamic/T36_MinDistance.java @@ -0,0 +1,132 @@ +package com.markilue.leecode.dynamic; + +import org.junit.Test; + +import java.util.Arrays; + +/** + *@BelongsProject: Leecode + *@BelongsPackage: com.markilue.leecode.dynamic + *@Author: dingjiawen + *@CreateTime: 2022-12-20 11:36 + *@Description: + * TODO 力扣72题 编辑距离: + * 给你两个单词 word1 和 word2, 请返回将 word1 转换成 word2 所使用的最少操作数 。 + * 你可以对一个单词进行如下三种操作: + * 插入一个字符 + * 删除一个字符 + * 替换一个字符 + *@Version: 1.0 + */ +public class T36_MinDistance { + + @Test + public void test(){ + String word1 = "horse"; + String word2 = "ros"; + System.out.println(minDistance(word1,word2)); + } + + @Test + public void test1(){ + String word1 = "intention"; + String word2 = "execution"; + System.out.println(minDistance(word1,word2)); + } + + /** + * 由于可以插入,可以替换,可以删除,本人没有明确的想法,这里先用距离推导状态转移方程 + * TODO 动态规划法: + * 1.dp定义:dp[i][j]表示word1[0-i]变成word2[0-j]所需最少步骤 + * 2.dp状态转移方程: + * 1.当word1[i]=word2[j]时,那么仅需要处理i,j之前的数 + * dp[i][j]=dp[i-1][j-1] + * 2.当word1[i]!=word2[j]时 + * 1)如果word1长度不够,他可能需要从word[j]添加一个数 dp[i][j-1]+1 + * 2)如果word1长度刚好,他可能需要在word[i-1][j-1]的基础上替换当前这个数 dp[i-1][j-1]+1 + * 3)如果word1长度超过,他直接把他删了,在dp[i-1][j]的基础上删除 dp[i-1][j]+1 + * dp[i][j]=min(dp[i][j-1]+1,dp[i-1][j-1]+1,dp[i-1][j]+1) + * 3.dp初始化:dp[0][i]=i;dp[i][0]=i + * 4.dp遍历顺序: + * 5.dp举例推导:以word1 = "horse", word2 = "ros"为例 + * [0 r o s] + * i=0: 0 1 2 3 + * i=h: 1 1 2 3 + * i=o: 2 2 1 2 + * i=r: 3 2 2 2 + * i=s: 4 3 3 2 + * i=e: 5 4 4 3 + * 速度击败95.89%,内存击败5.17% + * @param word1 + * @param word2 + * @return + */ + public int minDistance(String word1, String word2) { + char[] char1 = word1.toCharArray(); + char[] char2 = word2.toCharArray(); + int length1 = char1.length; + int length2 = char2.length; + + int[][] dp = new int[length1 + 1][length2 + 1]; + //初始化 + for (int i = 0; i < dp.length; i++) { + dp[i][0]=i; + } + for (int i = 0; i < dp[0].length; i++) { + dp[0][i]=i; + } + + for (int i = 1; i < dp.length; i++) { + for (int j = 1; j < dp[0].length; j++) { + if(char1[i-1]==char2[j-1]) dp[i][j]=dp[i-1][j-1]; + else dp[i][j]=Math.min(Math.min(dp[i][j-1]+1,dp[i-1][j-1]+1),dp[i-1][j]+1); + } + } + + return dp[length1][length2]; + + } + + + /** + * 官方题解中最快的方法,本质上还是dp,使用递归实现 + * 速度击败100%,内存击败5.17% 2ms + */ + int[][] meno ; + public int minDistance1(String word1, String word2){ + + meno = new int[word1.length()][word2.length()]; + + for (int[] ints : meno) { + Arrays.fill(ints,-1); + } + + return dp(word1,word1.length()-1,word2,word2.length()-1); + } + + private int dp(String word1, int i, String word2, int j) { + + if(i==-1){ + //i不够了,全添加到j + return j+1; + } + if(j==-1){ + //j不够了,把i全删了 + return i+1; + } + + if(meno[i][j]!=-1){ + return meno[i][j];//以前算过了,直接返回;有点像记忆化搜索 + } + + //状态转移方程 + if(word1.charAt(i)==word2.charAt(j)){ + meno[i][j] = dp(word1,i-1,word2,j-1); + + }else{ + meno[i][j] = Math.min(Math.min(dp(word1,i,word2,j-1),dp(word1,i-1,word2,j)),dp(word1,i-1,word2,j-1))+1; + } + + return meno[i][j]; + } +}