Hard草稿★★★★★O(m * n) 時間 · O(m) 空間
Dynamic ProgrammingString
Patterns🔲 網格・雙序列 DP
尚未複習過

解法已隱藏 — 先讀題目敘述、自己想想看,再點上方按鈕揭曉。

1639Number of Ways to Form a Target String Given a Dictionary2-D DPHard2-D DP

給定一組等長字串 words 和一個目標字串 target。每一步你可以選擇 words 中任一字串的某個位置的字元來匹配 target 的下一個字元,但選完後該位置及之前的所有位置都不能再使用。回傳組成 target 的方式數(對 10^9+7 取餘)。

Example:

Input: words = [“acca”,“bbbb”,“caca”], target = “aba” Output: 6

Intuition

TIP

先統計每個位置各字元的出現次數,再用 DP 計算匹配 target 的方式數。

Approaches

1. 2D DP — O(m * n) / O(m * n)
  • Idea: dp[i][j] 表示 target 的前 i 個字元用 words 的前 j 個位置匹配的方式數。
  • Time: O(m * n),m = target 長度,n = words[0] 長度
  • Space: O(m * n)
class Solution {
    fun numWays(words: Array<String>, target: String): Int {
        val MOD = 1_000_000_007L
        val n = words[0].length
        val m = target.length

        val freq = Array(n) { IntArray(26) }
        for (word in words) {
            for (j in word.indices) {
                freq[j][word[j] - 'a']++
            }
        }

        val dp = Array(m + 1) { LongArray(n + 1) }
        for (j in 0..n) dp[0][j] = 1

        for (i in 1..m) {
            for (j in 1..n) {
                dp[i][j] = dp[i][j - 1] // 跳過位置 j
                dp[i][j] = (dp[i][j] + dp[i - 1][j - 1] * freq[j - 1][target[i - 1] - 'a']) % MOD
            }
        }
        return dp[m][n].toInt()
    }
}
⭐ 2. 1D DP (Space Optimized) — O(m * n) / O(m)
  • Idea: 壓縮掉位置維度,逆序更新 target 維度。
  • Time: O(m * n)
  • Space: O(m)
class Solution {
    fun numWays(words: Array<String>, target: String): Int {
        val MOD = 1_000_000_007L
        val n = words[0].length
        val m = target.length

        val freq = Array(n) { IntArray(26) }
        for (word in words) {
            for (j in word.indices) {
                freq[j][word[j] - 'a']++
            }
        }

        val dp = LongArray(m + 1)
        dp[0] = 1

        for (j in 0 until n) {
            for (i in minOf(m, j + 1) downTo 1) {
                dp[i] = (dp[i] + dp[i - 1] * freq[j][target[i - 1] - 'a']) % MOD
            }
        }
        return dp[m].toInt()
    }
}

🔑 Takeaways