Medium草稿★★★★★O(k * m * n) 時間 · O(m * n) 空間
Dynamic ProgrammingKnapsack
Patterns🎒 背包🔲 網格・雙序列 DP
尚未複習過

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

474Ones and Zeroes2-D DPMedium2-D DP

給定一組二進位字串陣列 strs 和兩個整數 m(最多的 0 的數量)和 n(最多的 1 的數量),回傳最多能選幾個字串使得 0 的總數不超過 m、1 的總數不超過 n

Example:

Input: strs = [“10”,“0001”,“111001”,“1”,“0”], m = 5, n = 3 Output: 4 (選 “10”, “0001”, “1”, “0”)

Intuition

TIP

二維背包問題:每個字串有兩個「重量」(0 的數量和 1 的數量),背包有兩個容量限制。

Approaches

1. 3D DP — O(k * m * n) / O(k * m * n)
  • Idea: dp[k][i][j] 表示考慮前 k 個字串,使用最多 i 個 0 和 j 個 1 的最大子集大小。
  • Time: O(k * m * n)k 為字串數量
  • Space: O(k * m * n)
class Solution {
    fun findMaxForm(strs: Array<String>, m: Int, n: Int): Int {
        val k = strs.size
        val dp = Array(k + 1) { Array(m + 1) { IntArray(n + 1) } }

        for (idx in 1..k) {
            val zeros = strs[idx - 1].count { it == '0' }
            val ones = strs[idx - 1].length - zeros
            for (i in 0..m) {
                for (j in 0..n) {
                    dp[idx][i][j] = dp[idx - 1][i][j]
                    if (i >= zeros && j >= ones) {
                        dp[idx][i][j] = maxOf(dp[idx][i][j], dp[idx - 1][i - zeros][j - ones] + 1)
                    }
                }
            }
        }
        return dp[k][m][n]
    }
}
⭐ 2. 2D DP (Space Optimized) — O(k * m * n) / O(m * n)
  • Idea: 壓縮掉字串維度,逆序遍歷容量以保證每個字串只使用一次。
  • Time: O(k * m * n)
  • Space: O(m * n)
class Solution {
    fun findMaxForm(strs: Array<String>, m: Int, n: Int): Int {
        val dp = Array(m + 1) { IntArray(n + 1) }

        for (s in strs) {
            val zeros = s.count { it == '0' }
            val ones = s.length - zeros
            for (i in m downTo zeros) {
                for (j in n downTo ones) {
                    dp[i][j] = maxOf(dp[i][j], dp[i - zeros][j - ones] + 1)
                }
            }
        }
        return dp[m][n]
    }
}

🔑 Takeaways