115. 不同的子序列
題目連結:https://leetcode.cn/problems/distinct-subsequences/
文章講解:https://programmercarl.com/0115.不同的子序列.html
題目難度:困難
影片講解:https://www.bilibili.com/video/BV1fG4y1m75Q/
題目狀態:看題解
思路:
- 動態規劃陣列初始化
- 建立一個二維動態規劃陣列
dp
,大小為 ((s.size() + 1), (t.size() + 1))。dp[i][j]
表示s
的前i
個字元中包含t
的前j
個字元的不同子序列的數量。
- 建立一個二維動態規劃陣列
- 邊界條件設定
dp[i][0] = 1
:表示任何字串s
的前i
個字元都可以形成空字串t
的一種方式(即選擇不選)。dp[0][j] = 0
(對於 (j > 0)):表示空字串s
無法形成非空字串t
。
- 動規陣列更新
- 外層迴圈遍歷
s
的每個字元(從1
到s.size()
)。 - 內層迴圈遍歷
t
的每個字元(從1
到t.size()
)。 - 如果
s[i-1]
和t[j-1]
相等:dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j]
:表示可以選擇將這個字元包含在子序列中(加上dp[i-1][j-1]
)或者不包含(加上dp[i-1][j]
)。
- 如果不相等:
dp[i][j] = dp[i - 1][j]
:只能選擇不包含s[i-1]
。
- 外層迴圈遍歷
- 結果
- 返回
dp[s.size()][t.size()]
,即s
中包含t
的不同子序列的數量。
- 返回
程式碼:
class Solution {
public:
int numDistinct(string s, string t) {
int sLen = s.size(), tLen = t.size();
vector<vector<uint64_t>> dp(sLen + 1, vector<uint64_t>(tLen + 1));
for(int i = 0; i < sLen; ++i) dp[i][0] = 1;
for(int j = 1; j < tLen; ++j) dp[0][j] = 0;
for(int i = 1; i <= sLen; ++i) {
for(int j = 1; j <= tLen; ++j) {
if(s[i - 1] == t[j - 1]) dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j];
else dp[i][j] = dp[i - 1][j];
}
}
return dp[sLen][tLen];
}
};
583. 兩個字串的刪除操作
題目連結:https://leetcode.cn/problems/delete-operation-for-two-strings/
文章講解:https://programmercarl.com/0583.兩個字串的刪除操作.html
題目難度:中等
影片講解:https://www.bilibili.com/video/BV1we4y157wB/
題目狀態:看題解
思路:
維護一個二維動規陣列dp[i][j]
用來表示將word1
的前i
個字元轉換為word2
的前j
個字元所需的最小操作次數。
初始化dp[i][j]
:
dp[i][0] = i
:將word1
的前i
個字元轉換為空字串需要i
次刪除操作。dp[0][j] = j
:將空字串轉換為word2
的前j
個字元需要j
次插入操作。
更新動規陣列:
對於每一對i
和j
,我們考慮以下兩種情況:
- 字元匹配(
word1[i-1] == word2[j-1]
):- 如果當前字元相同,不需要任何操作:
dp[i][j] = dp[i - 1][j - 1];
- 如果當前字元相同,不需要任何操作:
- 字元不匹配(
word1[i-1] != word2[j-1]
):- 我們可以進行兩種操作:
- 刪除一次
word1
的元素:此時需要dp[i - 1][j] + 1
次操作次數。 - 刪除一次
word2
的元素:此時需要dp[i][j - 1] + 1
此操作次數。
- 刪除一次
- 取這兩種操作的最小值:
min(dp[i - 1][j] + 1, dp[i][j - 1] + 1)
- 我們可以進行兩種操作:
程式碼:
class Solution {
public:
int minDistance(string word1, string word2) {
int len1 = word1.size(), len2 = word2.size();
vector<vector<int>> dp(len1 + 1, vector<int>(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) {
for(int j = 1; j <= len2; ++j) {
if(word1[i - 1] == word2[j - 1]) dp[i][j] = dp[i - 1][j - 1];
else dp[i][j] = min(dp[i - 1][j] + 1, dp[i][j - 1] + 1);
}
}
return dp[len1][len2];
}
};
72. 編輯距離
題目連結:https://leetcode.cn/problems/edit-distance/
文章講解:https://programmercarl.com/0072.編輯距離.html
題目難度:中等
影片講解:https://www.bilibili.com/video/BV1qv4y1q78f/
題目狀態:有思路,細節出了問題
思路:
這道題的本質就是在上道題的基礎上做了改變,上道題遇到不相等的字元時只需要考慮刪除操作,這道題在遇到不相等的字元時需要考慮三種情況:刪除、插入、替換。
- 刪除:在
word1
中刪除一個字元,也就轉化為了word1[i - 2]
與word2[j - 1]
相比較了,因此dp[i][j] = dp[i - 1][j] + 1
。 - 插入:在
word1
中新增一個字元,其實這個操作得到的最終結果和在word2
中刪除一個字元的結果是一樣的,因此就轉化為word1[i - 1]
與word2[j - 2]
相比較了,因此dp[i][j] = dp[i][j - 1] + 1
。 - 替換:替換操作就是在
word1[i - 1] != word2[j - 1]
的前提下進行一次操作使得word1[i - 1] == word2[j - 1]
,因此dp[i][j] = dp[i - 1][j - 1] + 1
。
最終取這三個操作的最小值即可。
程式碼:
class Solution {
public:
int minDistance(string word1, string word2) {
int len1 = word1.size(), len2 = word2.size();
vector<vector<int>> dp(len1 + 1, vector<int>(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) {
for(int j = 1; j <= len2; ++j) {
if(word1[i - 1] == word2[j - 1]) dp[i][j] = dp[i - 1][j - 1];
else dp[i][j] = min({dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]}) + 1;
}
}
return dp[len1][len2];
}
};