902.最短編輯距離
題目描述
給定兩個字串 \(A\) 和 \(B\),現在要將 \(A\) 經過若干操作變為 \(B\),可進行的操作有:
- 刪除–將字串 \(A\) 中的某個字元刪除。
- 插入–在字串 \(A\) 的某個位置插入某個字元。
- 替換–將字串 \(A\) 中的某個字元替換為另一個字元。
現在請你求出,將 \(A\) 變為 \(B\) 至少需要進行多少次操作。
輸入描述
第一行包含整數 \(1\leq n \leq 1000\) ,表示字串 \(A\) 的長度。
第二行包含一個長度為 \(n\) 的字串 \(A\) 。
第三行包含整數 \(1\leq m \leq 1000\) ,表示字串 \(B\) 的長度。
第四行包含一個長度為 \(m\) 的字串 \(B\) 。
字串中均只包含大小寫字母。
輸出描述
輸出一個整數,表示最少操作次數。
解題思路
根據題目很容易想到暴力求解,但是複雜度顯然不允許,可以發現長字串的變化可以由短字串得到,因此可以考慮使用動態規劃來解決這個問題。
首先建立一個二維的dp陣列,一維刪除,一維插入。 \(dp[i][0]\) 表示將前 \(i\) 個字元的字串變為空字串所需的運算元,即刪除操作的次數; \(dp[0][i]\) 表示將空字串變為前 \(i\) 個字元的字串所需的運算元,即插入操作的次數。總的來說, \(dp[i][j]\) 表示把 \(s1[1]\) - \(s1[i]\) 變成 \(s2[1]\) - \(s2[j]\) 需要操作的最少操作次數。
初始化 \(dp[0][0]\) 為 \(0\) ,表示從空字串到空字串需要操作的次數是 \(0\) 。
對於每個字元 \(s1[i]\) 和 \(s2[j]\) ,如果它們不同,則可以有三種操作:
- 刪除 \(s1[i]\) ,即 \(dp[i-1][j]+1\)
- 插入 \(s2[j]\) ,即 \(dp[i][j-1]+1\)
- 替換 \(s1[i]\) 為 \(s2[j]\) :即 \(dp[i-1][j-1]+1\)
如果它們相同,則不需要進行操作,運算元為 \(dp[i-1][j-1]\)
因此我們可以得到以下狀態轉移方程:
\[dp[i][j]=min
\begin{cases}
dp[i−1][j]+1 , (刪除操作) \\
dp[i][j−1]+1 , (插入操作) \\
dp[i−1][j−1]+cost , (替換操作,如果s1[i-1]==s2[j-1],cost=0,否則cost=1)
\end{cases}
\]
結合上述步驟即可得到最終答案。
程式碼實現
#include <bits/stdc++.h>
using i64 = long long;
const int inf = 2e9;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
int n, m;
std::string s1, s2;
std::cin >> n >> s1 >> m >> s2;
s1 = " " + s1;
s2 = " " + s2;
std::vector<std::vector<int>> dp(n + 1, std::vector<int>(m + 1, inf));
for (int i = 1; i <= n; i++) {
dp[i][0] = i;
}
for (int i = 1; i <= m; i++) {
dp[0][i] = i;
}
dp[0][0] = 0;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
dp[i][j] = std::min(dp[i - 1][j], dp[i][j - 1]) + 1;
if (s1[i] == s2[j]) {
dp[i][j] = std::min(dp[i][j], dp[i - 1][j - 1]);
} else {
dp[i][j] = std::min(dp[i][j], dp[i - 1][j - 1] + 1);
}
}
}
std::cout << dp[n][m];
}