Edit Distance

題目描述

給定兩個字串 word1word2,回傳將 word1 轉換為 word2 所需的最少操作數。每次可以插入、刪除或替換一個字元。

輸入:word1 = "horse", word2 = "ros"
輸出:3
解釋:horse → rorse(替換 h→r)→ rose(刪除 r)→ ros(刪除 e)

輸入:word1 = "intention", word2 = "execution"
輸出:5

解題思路

1

定義狀態

dp[i][j] = word1[0..i-1] 轉換成 word2[0..j-1] 的最少操作數

2

邊界條件

dp[i][0] = i(全刪除),dp[0][j] = j(全插入)

3

字元相同

dp[i][j] = dp[i-1][j-1](不需要操作)

4

字元不同

dp[i][j] = 1 + min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1])

5

三種操作

刪除=dp[i-1][j], 插入=dp[i][j-1], 替換=dp[i-1][j-1]

程式碼

C#

csharp
public class Solution {
    public int MinDistance(string word1, string word2) {
        int m = word1.Length, n = word2.Length;
        int[] dp = new int[n + 1];
        
        // 初始化:空字串到 word2[0..j-1] 需要 j 次插入
        for (int j = 0; j <= n; j++) dp[j] = j;
        
        for (int i = 1; i <= m; i++) {
            int prev = dp[0]; // dp[i-1][j-1]
            dp[0] = i;        // 空字串需要 i 次刪除
            for (int j = 1; j <= n; j++) {
                int temp = dp[j];
                if (word1[i - 1] == word2[j - 1]) {
                    dp[j] = prev;
                } else {
                    dp[j] = 1 + Math.Min(Math.Min(dp[j], dp[j - 1]), prev);
                    // dp[j]=刪除, dp[j-1]=插入, prev=替換
                }
                prev = temp;
            }
        }
        return dp[n];
    }
}

TypeScript

typescript
function minDistance(word1: string, word2: string): number {
    const m = word1.length, n = word2.length;
    const dp: number[][] = Array.from({length: m + 1}, (_, i) =>
        Array.from({length: n + 1}, (_, j) => (i === 0 ? j : j === 0 ? i : 0))
    );
    
    for (let i = 1; i <= m; i++) {
        for (let j = 1; j <= n; j++) {
            if (word1[i - 1] === word2[j - 1]) {
                dp[i][j] = dp[i - 1][j - 1];
            } else {
                dp[i][j] = 1 + Math.min(
                    dp[i - 1][j],     // 刪除
                    dp[i][j - 1],     // 插入
                    dp[i - 1][j - 1]  // 替換
                );
            }
        }
    }
    return dp[m][n];
}

Python

python
def minDistance(word1: str, word2: str) -> int:
    m, n = len(word1), len(word2)
    dp = list(range(n + 1))  # dp[j] = j(初始化)
    
    for i in range(1, m + 1):
        prev = dp[0]
        dp[0] = i
        for j in range(1, n + 1):
            temp = dp[j]
            if word1[i - 1] == word2[j - 1]:
                dp[j] = prev
            else:
                dp[j] = 1 + min(dp[j], dp[j - 1], prev)
            prev = temp
    
    return dp[n]

複雜度分析

| | Time | Space | |--|------|-------| | 本解法 | O(m × n) | O(n)(空間優化後) |

圖解

字元相同
直接繼承
字元不同
替換
dp[i-1][j-1] (替換/不動)
+0
dp[i-1][j] (刪除)
min+1
dp[i][j-1] (插入)
min+1
dp[i][j]

進階觀點

💡面試追問

  • Edit Distance(Levenshtein Distance)在拼字檢查、DNA 比對、搜尋引擎模糊匹配中廣泛使用。
  • 如果要輸出具體的操作序列,需要從 dp 表格反向追溯。
  • 583. Delete Operation for Two Strings 是只允許刪除的簡化版本,等價於 m + n - 2 × LCS。
  • 進階優化:如果兩個字串長度差很大,可以只計算對角帶狀區域(Ukkonen's algorithm)。

理解測驗

🤔 dp[i-1][j] + 1 對應哪種操作?

🤔 為什麼字元相同時不需要額外操作(dp[i][j] = dp[i-1][j-1])?

你可能也想看

714. Best Time to Buy and Sell Stock with Transaction Fee338. Counting Bits

按 ← → 鍵切換課程