115. 不同的子序列
题目描述
给定一个字符串 s 和一个字符串 t ,计算在 s 的子序列中 t 出现的个数。
字符串的一个子序列是指,通过删除一些(也可以不删除)字符且不干扰剩余字符相对位置所组成的新字符串。(例如,“ACE” 是 “ABCDE” 的一个子序列,而 “AEC” 不是)
题目数据保证答案符合 32 位带符号整数范围。
解题思路
这个问题同样可以通过动态规划来解决。我们定义一个二维数组 dp,其中 dp[i][j] 表示字符串 s 的前 i 个字符中包含字符串 t 的前 j 个字符的子序列的个数。
递推公式的由来:
- 当 s 的第 i 个字符(从 1 开始计数)与 t 的第 j 个字符相同时,我们可以选择使用这个字符来匹配 t 的第 j 个字符,或者不使用这个字符。因此,dp[i][j] 应该是 dp[i-1][j-1](使用 s 的第 i 个字符匹配 t 的第 j 个字符)加上 dp[i-1][j](不使用 s 的第 i 个字符)。
- 当 s 的第 i 个字符与 t 的第 j 个字符不同时,我们不能使用 s 的第 i 个字符来匹配 t 的第 j 个字符,因此 dp[i][j] 应该等于 dp[i-1][j]。
初始化条件: - dp[i][0] 应该为 1,因为任何字符串的子序列中都包含空字符串。
- dp[0][j] 应该为 0,除非 j 也为 0,因为空字符串不能包含非空字符串作为子序列。
例子说明:
假设 s = “rabbbit”,t = “rabbit”。我们要计算 dp[7][6]。 - dp[7][6] = dp[6][5] + dp[6][6],因为 s 的最后一个字符 ‘t’ 与 t 的最后一个字符 ‘t’ 相同,我们可以选择使用它来匹配,或者不使用它。
代码
class Solution {
public int numDistinct(String s, String t) {
int[][] dp = new int[s.length()+1][t.length()+1];
for (int i = 0; i <= s.length(); i++) {
dp[i][0] = 1;
}
for (int i = 1; i <= s.length(); i++) {
for (int j = 1; j <= t.length(); j++){
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()];
}
}
583. 两个字符串的删除操作
题目描述
给定两个单词 word1
和 word2
,找到使得 word1
和 word2
相同所需的最小步数,每步可以删除任意一个字符串中的一个字符。
示例:
- 输入: “sea”, “eat”
- 输出: 2
- 解释: 第一步将"sea"变为"ea",第二步将"eat"变为"ea"
解题思路
这个问题可以通过动态规划来解决。我们定义一个二维数组 dp
,其中 dp[i][j]
表示将字符串 word1
的前 i
个字符和字符串 word2
的前 j
个字符转换为相同字符串所需的最小删除操作数。
递推公式的由来:
- 当
word1
的第i
个字符(从 1 开始计数)与word2
的第j
个字符相同时,我们不需要进行任何删除操作,因此dp[i][j]
应该等于dp[i-1][j-1]
。 - 当
word1
的第i
个字符与word2
的第j
个字符不同时,我们有三种选择:- 删除
word1
的第i
个字符,此时操作数增加 1,即dp[i-1][j] + 1
。 - 删除
word2
的第j
个字符,此时操作数增加 1,即dp[i][j-1] + 1
。 - 同时删除
word1
的第i
个字符和word2
的第j
个字符,此时操作数增加 2,但这种情况不是最优的,因为我们总是可以选择删除一个字符来达到相同的效果。
因此,我们只需要在删除word1
的第i
个字符和删除word2
的第j
个字符中选择操作数较少的一个,即dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + 1
。
初始化条件:
- 删除
dp[i][0]
应该等于i
,因为将word1
的前i
个字符转换为空字符串需要删除i
个字符。dp[0][j]
应该等于j
,因为将空字符串转换为word2
的前j
个字符需要插入j
个字符,但在这个问题中我们只考虑删除操作,所以初始化为j
。
代码
class Solution {
public int minDistance(String word1, String word2) {
int[][] dp = new int[word1.length() + 1][word2.length() + 1];
for (int i = 0; i <= word1.length(); i++) {
dp[i][0] = i;
}
for (int i = 0; i <= word2.length(); i++) {
dp[0][i] = i;
}
for (int i = 1; i <= word1.length(); i++) {
for (int j = 1; j <= word2.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], dp[i][j - 1]) + 1;
}
}
}
return dp[word1.length()][word2.length()];
}
}
72. 编辑距离
题目描述
给你两个单词 word1
和 word2
,请你计算出将 word1
转换成 word2
所使用的最少操作数。
你可以对一个单词进行如下三种操作:
- 插入一个字符
- 删除一个字符
- 替换一个字符
示例 1: - 输入:
word1 = "horse"
,word2 = "ros"
- 输出:3
- 解释:
horse -> rorse (将 'h' 替换为 'r') -> rose (删除 'r') -> ros (删除 'e')
示例 2: - 输入:
word1 = "intention"
,word2 = "execution"
- 输出:5
- 解释:
intention -> inention (删除 't') -> enention (将 'i' 替换为 'e') -> exention (将 'n' 替换为 'x') -> exection (将 'n' 替换为 'c') -> execution (插入 'u')
解题思路
这个问题可以通过动态规划来解决。我们定义一个二维数组 dp
,其中 dp[i][j]
表示将字符串 word1
的前 i
个字符转换为字符串 word2
的前 j
个字符所需的最少操作数。
递推公式的由来:
- 当
word1
的第i
个字符与word2
的第j
个字符相同时,我们不需要进行任何操作,因此dp[i][j]
应该等于dp[i-1][j-1]
。 - 当
word1
的第i
个字符与word2
的第j
个字符不同时,我们有三种选择:- 插入操作:在
word1
中插入一个字符以匹配word2
的第j
个字符,此时操作数增加 1,即dp[i][j-1] + 1
。 - 删除操作:删除
word1
的第i
个字符,此时操作数增加 1,即dp[i-1][j] + 1
。 - 替换操作:将
word1
的第i
个字符替换为word2
的第j
个字符,此时操作数增加 1,即dp[i-1][j-1] + 1
。
- 插入操作:在
- 因此,
dp[i][j]
应该是上述三种操作的最小值。
初始化: dp[0][j]
表示将空字符串转换为word2
的前j
个字符所需的操作数,即插入j
个字符,所以dp[0][j] = j
。dp[i][0]
表示将word1
的前i
个字符转换为空字符串所需的操作数,即删除i
个字符,所以dp[i][0] = i
。
代码
class Solution {
public int minDistance(String word1, String word2) {
int[][] dp = new int[word1.length() + 1][word2.length() + 1];
for (int i = 0; i <= word1.length(); i++) {
dp[i][0] = i;
}
for (int i = 0; i <= word2.length(); i++) {
dp[0][i] = i;
}
for (int i = 1; i <= word1.length(); i++) {
for (int j = 1; j <= word2.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], dp[i][j - 1]), dp[i - 1][j - 1]) + 1;
}
}
}
return dp[word1.length()][word2.length()];
}
}