diff --git a/Leecode/src/main/java/com/markilue/leecode/dynamic/second/T34_NumDistinct.java b/Leecode/src/main/java/com/markilue/leecode/dynamic/second/T34_NumDistinct.java index 00831ef..149a061 100644 --- a/Leecode/src/main/java/com/markilue/leecode/dynamic/second/T34_NumDistinct.java +++ b/Leecode/src/main/java/com/markilue/leecode/dynamic/second/T34_NumDistinct.java @@ -1,5 +1,7 @@ package com.markilue.leecode.dynamic.second; +import org.junit.Test; + /** *@BelongsProject: Leecode *@BelongsPackage: com.markilue.leecode.dynamic.second @@ -15,7 +17,78 @@ package com.markilue.leecode.dynamic.second; */ public class T34_NumDistinct { + @Test + public void test(){ + String s = "rabbbit", t = "rabbit"; +// String s = "babgbag", t = "bag"; + System.out.println(numDistinct1(s,t)); + } + + /** + * 思路:本质上实际上是从是不是子序列变为了 有多少个子序列的问题 + * TODO 不同的子序列: + * 1.dp定义: dp[i][j]表示使用s[0-i]能凑出多少个t[0-j] + * 2.dp状态转移方程: + * dp[i][j]的状态分为两种 : + * if s[i]==t[j] 原本能继续凑出 dp[i][j]=dp[i-1][j-1]+dp[i-1][j] + * else 不能继续凑出 dp[i][j]=dp[i-1][j] + * 3.dp初始化: + * 4.dp遍历顺序: + * 5.dp举例推导:以s = "rabbbit", t = "rabbit"为例 + * [0 r a b b i t] + * 0: 1 0 0 0 0 0 0 + * r: 1 1 0 0 0 0 0 + * a: 1 1 1 0 0 0 0 + * b: 1 1 1 1 0 0 0 + * b: 1 1 1 2 1 0 0 + * b: 1 1 1 3 3 0 0 + * i: 1 1 1 3 3 3 0 + * t: 1 1 1 3 3 3 3 + * 速度击败26.62% 内存击败32.5% 16ms + * @param s + * @param t + * @return + */ public int numDistinct(String s, String t) { + int[][] dp = new int[s.length() + 1][t.length() + 1]; + + for (int i = 0; i < dp.length; i++) { + dp[i][0] = 1; + } + + for (int i = 1; i < dp.length; i++) { + for (int j = 1; j < dp[0].length; j++) { + //证明之前匹配上过,那么在之前匹配上的基础上+后来匹配上的次数:一部分是用s[i - 1]来匹配;一部分是不用s[i - 1]来匹配 + if (s.charAt(i - 1) == t.charAt(j - 1)) dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j]; + else dp[i][j] = dp[i - 1][j]; + } + } + return dp[s.length()][t.length()]; + + } + + + /** + * 一维dp优化 + * 速度击败73.25% 内存击败99.63% 11ms + * @param s + * @param t + * @return + */ + public int numDistinct1(String s, String t) { + + int[] dp = new int[t.length() + 1]; + + dp[0] = 1; + + + for (int i = 1; i < s.length()+1; i++) { + for (int j = t.length(); j >0; j--) {//因为要使用之前的j-1所以就反向遍历 + if (s.charAt(i - 1) == t.charAt(j - 1)) dp[j] += dp[j - 1]; + } + } + return dp[t.length()]; + } } diff --git a/Leecode/src/main/java/com/markilue/leecode/dynamic/second/T35_MinDistance.java b/Leecode/src/main/java/com/markilue/leecode/dynamic/second/T35_MinDistance.java new file mode 100644 index 0000000..2c162c1 --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/dynamic/second/T35_MinDistance.java @@ -0,0 +1,75 @@ +package com.markilue.leecode.dynamic.second; + +import org.junit.Test; + +/** + *@BelongsProject: Leecode + *@BelongsPackage: com.markilue.leecode.dynamic.second + *@Author: markilue + *@CreateTime: 2023-02-24 10:32 + *@Description: + * TODO 力扣583题 两个字符串的删除操作: + * 给定两个单词 word1 和 word2 ,返回使得 word1 和 word2 相同所需的最小步数。 + * 每步 可以删除任意一个字符串中的一个字符。 + *@Version: 1.0 + */ +public class T35_MinDistance { + + @Test + public void test(){ + String word1 = "leetcode", word2 = "etco"; + System.out.println(minDistance(word1,word2)); + } + + /** + * 思路: 每个字符串都能删 + * TODO DP五部曲: + * 1.dp定义:dp[i][j]表示使用word1[i]变为word2[j]需要删除的数量 + * 2.dp状态转移方程: + * 1.当word1[i]==word2[j]时 则不需要多加操作 + * dp[i][j]=dp[i-1][j-1] + * 2.当word1[i]!=word2[j]时 则需要在之前多加操作 + * dp[i][j]= min(dp[i-1][j]+1,dp[i][j-1]+1) + * 3.dp初始化: dp[i][0]=i dp[0][j]=j + * 4.dp遍历顺序: + * 5.dp举例推导:以word1 = "leetcode", word2 = "etco"为例 + * [0 e t c o] + * 0 0 1 2 3 4 + * l 1 2 3 4 5 + * e 2 1 2 3 4 + * e 3 + * t 4 + * c 5 + * o 6 + * d 7 + * e 8 + * 速度击败62.53% 内存击败39.37% 7ms + * @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 = 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 (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()]; + + } + + + +} diff --git a/Leecode/src/main/java/com/markilue/leecode/dynamic/second/T36_MinDistance.java b/Leecode/src/main/java/com/markilue/leecode/dynamic/second/T36_MinDistance.java new file mode 100644 index 0000000..47f6aee --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/dynamic/second/T36_MinDistance.java @@ -0,0 +1,96 @@ +package com.markilue.leecode.dynamic.second; + +import org.junit.Test; + +/** + *@BelongsProject: Leecode + *@BelongsPackage: com.markilue.leecode.dynamic.second + *@Author: markilue + *@CreateTime: 2023-02-24 11:09 + *@Description: + * TODO 力扣72题 编辑距离: + * 给你两个单词 word1 和 word2, 请返回将 word1 转换成 word2 所使用的最少操作数 。 + * 你可以对一个单词进行如下三种操作: + * 插入一个字符 + * 删除一个字符 + * 替换一个字符 + *@Version: 1.0 + */ +public class T36_MinDistance { + + @Test + public void test(){ + String word1 = "horse", word2 = "ros"; + System.out.println(minDistance(word1,word2)); + } + + /** + * 思路:更复杂的地方在于 编辑存在三种操作: 应该如何处理 + * TODO dp五部曲: + * 1.dp定义:dp[i][j]表示word1[0-i]通过三种操作最少能花多少步变成word2[0-j] + * 2.dp状态转移方程: + * 1.当word1[i]!=word1[j]时 删除 修改 插入 + * dp[i][j]=min(dp[i-1][j]+1,dp[i-1][j-1]+1,dp[i][j-1]+1) + * else dp[i][j]=dp[i-1][j-1] + * 3.dp初始化: + * 4.dp遍历顺序: + * 5.dp举例推导: + * 速度击败45.95% 内存击败14.7% 5ms + * @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 = 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(word1.charAt(i-1)==word2.charAt(j-1)) dp[i][j]=dp[i-1][j-1]; + else dp[i][j]=Math.min(Math.min(dp[i-1][j]+1,dp[i-1][j-1]+1),dp[i][j-1]+1); + } + } + + return dp[word1.length()][word2.length()]; + } + + + /** + * toCharArray()优化 + * 速度击败95.84% 内存击败60.85% + * @param word1 + * @param word2 + * @return + */ + public int minDistance1(String word1, String word2) { + + int[][] dp = new int[word1.length() + 1][word2.length() + 1]; + char[] char1 = word1.toCharArray(); + char[] char2 = word2.toCharArray(); + + 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-1][j]+1,dp[i-1][j-1]+1),dp[i][j-1]+1); + } + } + + return dp[word1.length()][word2.length()]; + } +}