LeetCode 115.不同的子序列
题目链接:https://leetcode.cn/problems/distinct-subsequences/description/
文章链接:https://programmercarl.com/0115.%E4%B8%8D%E5%90%8C%E7%9A%84%E5%AD%90%E5%BA%8F%E5%88%97.html
思路
* dp[i][j]:以i-1为结尾的s子序列中出现以j-1为结尾的t的个数为dp[i][j]。* 这一类问题,基本是要分析两种情况* s[i - 1] 与 t[j - 1]相等* s[i - 1] 与 t[j - 1] 不相等* (1)当s[i - 1] 与 t[j - 1]相等时,dp[i][j]可以有两部分组成。* 一部分是用s[i - 1]来匹配,那么个数为dp[i - 1][j - 1]。即不需要考虑当前s子串和t子串的最后一位字母,所以只需要 dp[i-1][j-1]。* 一部分是不用s[i - 1]来匹配,个数为dp[i - 1][j]。* 例如: s:bagg 和 t:bag ,s[3] 和 t[2]是相同的,但是字符串s也可以不用s[3]来匹配,即用s[0]s[1]s[2]组成的bag。* 当然也可以用s[3]来匹配,即:s[0]s[1]s[3]组成的bag。* 所以当s[i - 1] 与 t[j - 1]相等时,dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j];* (2)当s[i - 1] 与 t[j - 1]不相等时,dp[i][j]只有一部分组成,不用s[i - 1]来匹配(就是模拟在s中删除这个元素),即:dp[i - 1][j]* 所以递推公式为:dp[i][j] = dp[i - 1][j];
public int numDistinct(String s, String t) {int len1 = s.length();int len2 = t.length();int[][] dp = new int[len1 + 1][len2 + 1];for (int i = 0; i <= len1; i++) {dp[i][0] = 1;}for (int i = 1; i <= len1 ; i++) {char t1 = s.charAt(i);for (int j = 1; j <= len2 ; j++) {char t2 = t.charAt(j);if (t1 == t2)dp[i][j] = dp[i-1][j-1] + dp[i-1][j];elsedp[i][j] = dp[i-1][j];}}return dp[len1][len2];}
LeetCode 583. 两个字符串的删除操作
题目链接:https://leetcode.cn/problems/delete-operation-for-two-strings/description/
文章链接:https://programmercarl.com/0583.%E4%B8%A4%E4%B8%AA%E5%AD%97%E7%AC%A6%E4%B8%B2%E7%9A%84%E5%88%A0%E9%99%A4%E6%93%8D%E4%BD%9C.html#%E7%AE%97%E6%B3%95%E5%85%AC%E5%BC%80%E8%AF%BE
思路
* dp[i][j]:以i-1为结尾的字符串word1,和以j-1位结尾的字符串word2,想要达到相等,所需要删除元素的最少次数* 当word1[i - 1] 与 word2[j - 1]相同的时候* 当word1[i - 1] 与 word2[j - 1]不相同的时候* 当word1[i - 1] 与 word2[j - 1]相同的时候,dp[i][j] = dp[i - 1][j - 1];* 当word1[i - 1] 与 word2[j - 1]不相同的时候,有三种情况:* 情况一:删word1[i - 1],最少操作次数为dp[i - 1][j] + 1* 情况二:删word2[j - 1],最少操作次数为dp[i][j - 1] + 1* 情况三:同时删word1[i - 1]和word2[j - 1],操作的最少次数为dp[i - 1][j - 1] + 2* 那最后当然是取最小值,所以当word1[i - 1] 与 word2[j - 1]不相同的时候,递推公式:dp[i][j] = min({dp[i - 1][j - 1] + 2, dp[i - 1][j] + 1, dp[i][j - 1] + 1});* 因为 dp[i][j - 1] + 1 = dp[i - 1][j - 1] + 2,所以递推公式可简化为:dp[i][j] = min(dp[i - 1][j] + 1, dp[i][j - 1] + 1)
public int minDistance(String word1, String word2) {int len1 = word1.length();int len2 = word2.length();int[][] dp = new int[len1 + 1][len2 + 1];for (int i = 0; i <= len1; i++) {dp[i][0] = i;}for (int j = 0; j <= len2; j++) {dp[0][j] = j;}for (int i = 1; i <= len1; i++) {char t1 = word1.charAt(i);for (int j = 1; j <= len2; j++) {char t2 = word2.charAt(j);if (t1 == t2)dp[i][j] = dp[i - 1][j - 1];elsedp[i][j] = Math.min(dp[i - 1][j] + 1, dp[i][j - 1] + 1);}}return dp[len1][len2];}
LeetCode 72. 编辑距离
题目链接:https://leetcode.cn/problems/edit-distance/description/
文章链接:https://programmercarl.com/0072.%E7%BC%96%E8%BE%91%E8%B7%9D%E7%A6%BB.html#%E7%AE%97%E6%B3%95%E5%85%AC%E5%BC%80%E8%AF%BE
思路
* dp[i][j] 表示以下标i-1为结尾的字符串word1,和以下标j-1为结尾的字符串word2,最近编辑距离为dp[i][j]* (1)if (word1[i - 1] == word2[j - 1]) 那么说明不用任何编辑,dp[i][j] 就应该是 dp[i - 1][j - 1],即dp[i][j] = dp[i - 1][j - 1];* (2)if (word1[i - 1] != word2[j - 1]),此时就需要编辑了* 操作一:word1删除一个元素,那么就是以下标i - 2为结尾的word1 与 j-1为结尾的word2的最近编辑距离 再加上一个操作。dp[i][j] = dp[i - 1][j] + 1;* 操作二:word2删除一个元素,那么就是以下标i - 1为结尾的word1 与 j-2为结尾的word2的最近编辑距离 再加上一个操作。即 dp[i][j] = dp[i][j - 1] + 1;* 添加操作和删除操作是一样的,例如:word2添加一个元素,相当于word1删除一个元素,例如 word1 = "ad" ,word2 = "a",word1删除元素'd' 和 word2添加一个元素'd',变成word1="a", word2="ad", 最终的操作数是一样!* 操作三:替换元素,word1替换word1[i - 1],使其与word2[j - 1]相同,此时不用增删加元素。* 可以回顾一下,if (word1[i - 1] == word2[j - 1])的时候我们的操作 是 dp[i][j] = dp[i - 1][j - 1] 对吧。* 那么只需要一次替换的操作,就可以让 word1[i - 1] 和 word2[j - 1] 相同。* 所以 dp[i][j] = dp[i - 1][j - 1] + 1;
public int minDistance(String word1, String word2) {int len1 = word1.length();int len2 = word2.length();int[][] dp = new int[len1 + 1][len2 + 1];for (int i = 1; i <= len1; i++) {dp[i][0] = i;}for (int j = 1; j <= len2; j++) {dp[0][j] = j;}for (int i = 1; i <= len1; i++) {char t1 = word1.charAt(i);for (int j = 1; j <= len2; j++) {char t2 = word2.charAt(j);if (t1 == t2)dp[i][j] = dp[i - 1][j - 1];elsedp[i][j] = Math.min(Math.min(dp[i - 1][j] + 1, dp[i][j - 1] + 1), dp[i - 1][j - 1] + 1);}}return dp[len1][len2];}