Description

給定兩個字串 word1word2,回傳將 word1 轉換成 word2 所需的最少操作次數。允許的操作:插入一個字元、刪除一個字元、替換一個字元。

Example:

Input: word1 = “horse”, word2 = “ros” Output: 3 (horse -> rorse -> rose -> ros)

Intuition#

  • word1[i-1] == word2[j-1]dp[i][j] = dp[i-1][j-1](不需操作)
  • 否則取三種操作的最小值 +1:
    • dp[i-1][j-1] + 1:替換
    • dp[i-1][j] + 1:刪除 word1 的字元
    • dp[i][j-1] + 1:插入字元到 word1
  • 基底:dp[i][0] = i(全部刪除),dp[0][j] = j(全部插入)

Approaches#

1: 2D DP Table#

  • 概念: 建立 (m+1) x (n+1) 的表格,按照轉移方程填寫。
  • 時間複雜度: O(m * n)
  • 空間複雜度: O(m * n)
class Solution {
    fun minDistance(word1: String, word2: String): Int {
        val m = word1.length
        val n = word2.length
        val dp = Array(m + 1) { IntArray(n + 1) }
        for (i in 0..m) dp[i][0] = i
        for (j in 0..n) dp[0][j] = j
        for (i in 1..m) {
            for (j in 1..n) {
                dp[i][j] = if (word1[i - 1] == word2[j - 1]) {
                    dp[i - 1][j - 1]
                } else {
                    minOf(dp[i - 1][j - 1], dp[i - 1][j], dp[i][j - 1]) + 1
                }
            }
        }
        return dp[m][n]
    }
}

⭐2: 1D DP(空間優化)#

  • 概念: 用一維陣列加 prev 變數保存左上角的值。
  • 時間複雜度: O(m * n)
  • 空間複雜度: O(n)
class Solution {
    fun minDistance(word1: String, word2: String): Int {
        val m = word1.length
        val n = word2.length
        val dp = IntArray(n + 1) { it }
        for (i in 1..m) {
            var prev = dp[0]
            dp[0] = i
            for (j in 1..n) {
                val temp = dp[j]
                dp[j] = if (word1[i - 1] == word2[j - 1]) {
                    prev
                } else {
                    minOf(prev, dp[j], dp[j - 1]) + 1
                }
                prev = temp
            }
        }
        return dp[n]
    }
}

🔑 Takeaways#

  • Pattern: 雙字串 DP — 插入/刪除/替換三種操作對應三個方向的轉移
  • 關鍵技巧: Edit Distance 是 NLP、拼寫檢查的基礎演算法;空間優化的 prev 變數技巧在多題中重複出現