6.7 字符串編輯
72. Edit Distance
題目描述
給定兩個字符串,已知你可以刪除、替換和插入任意字符串的任意字符,求最少編輯幾步可以將兩個字符串變成相同。
輸入輸出範例
輸入是兩個字符串,輸出是一個整數,表示最少的步驟。
Input: word1 = "horse", word2 = "ros"
Output: 3
在這個範例中,一種最優編輯方法是 horse -> rorse -> rose -> ros。
題解
類似於題目 1143,我們使用一個二維陣列 dp[i][j]
,表示將第一個字符串到位置 i
為止,和第二個字符串到位置 j
為止,最多需要幾步編輯。
- 當第
i
位和第j
位對應的字符相同時,dp[i][j] = dp[i-1][j-1]
; - 當兩者對應的字符不相同時:
- 修改的消耗為
dp[i-1][j-1] + 1
; - 插入
i
位置/刪除j
位置的消耗為dp[i][j-1] + 1
; - 插入
j
位置/刪除i
位置的消耗為dp[i-1][j] + 1
。
- 修改的消耗為
- C++
- Python
int minDistance(string word1, string word2) {
int m = word1.length(), n = word2.length();
vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0));
for (int i = 0; i <= m; ++i) {
for (int j = 0; j <= n; ++j) {
if (i == 0 || j == 0) {
dp[i][j] = i + j;
} else {
dp[i][j] = dp[i - 1][j - 1] + (word1[i - 1] != word2[j - 1]);
dp[i][j] = min(dp[i][j], dp[i - 1][j] + 1);
dp[i][j] = min(dp[i][j], dp[i][j - 1] + 1);
}
}
}
return dp[m][n];
}
def minDistance(word1: str, word2: str) -> int:
m, n = len(word1), len(word2)
dp = [[0 for _ in range(n + 1)] for _ in range(m + 1)]
for i in range(m + 1):
for j in range(n + 1):
if i == 0 or j == 0:
dp[i][j] = i + j
else:
dp[i][j] = min(
dp[i - 1][j - 1] + int(word1[i - 1] != word2[j - 1]),
dp[i][j - 1] + 1,
dp[i - 1][j] + 1,
)
return dp[m][n]
650. 2 Keys Keyboard
題目描述
給定一個字母 A,已知你可以每次選擇複製全部字符,或者粘貼之前複製的字符,求最少需要幾次操作可以把字符串延展到指定長度。
輸入輸出範例
輸入是一個正整數,代表指定長度;輸出是一個整數,表示最少操作次數。
Input: 3
Output: 3
在這個範例中,一種最優的操作方法是先複製一次,再貼上兩次。
題解
不同於以往通過加減實現的動態規劃,這裡需要乘除法來計算位置,因為粘貼操作是倍數增加的。我們使用一個一維陣列 dp,其中位置 i 表示延展到長度 i 的最少操作次數。對於每個位置 j,如果 j 可以被 i 整除,那麼長度 i 就可以由長度 j 操作得到,其操作次數等價於把一個長度為 j 的 A 延展到長度為 i/j。因此我們可以得到遞推公式 dp[i] = dp[j] + dp[i/j]。
- C++
- Python
int minSteps(int n) {
vector<int> dp(n + 1, 0);
for (int i = 2; i <= n; ++i) {
dp[i] = i;
for (int j = 2; j * j <= i; ++j) {
if (i % j == 0) {
dp[i] = dp[j] + dp[i / j];
// 提前剪枝,因為j從小到大,因此操作數量一定最小。
break;
}
}
}
return dp[n];
}