Skip to main content

一和零

Tips

题目类型: Dynamic Programming

题目

给你一个二进制字符串数组 strs 和两个整数 mn. 请你找出并返回 strs 的最大子集的长度, 该子集中最多m0n1. 如果 x 的所有元素也是 y 的元素, 集合 x 是集合 y子集.

提示:
  • 1 <= strs.length <= 600
  • 1 <= strs[i].length <= 100
  • strs[i] 仅由 '0''1' 组成
  • 1 <= m, n <= 100
示例
输入: strs = ["10", "0001", "111001", "1", "0"], m = 5, n = 3
输出: 4
解释:
最多有 5031 的最大子集是 {"10", "0001", "1", "0"}, 因此答案是 4.
其他满足题意但较小的子集包括 {"0001", "1"}{"10", "1", "0"}.
{"111001"} 不满足题意, 因为它含 41, 大于 n 的值 3.
输入: strs = ["10", "0", "1"], m = 1, n = 1
输出: 2
解释: 最大的子集是 {"0", "1"}, 所以答案是 2.

题解

典型的 0-1 背包 问题, 由于要往 mn 两个背包放, 所以要写成三维 dp, 当然可以降到二维 dp.

/**
* @param {string[]} strs
* @param {number} m
* @param {number} n
* @return {number}
*/
var findMaxForm = function (strs, m, n) {
const len = strs.length

// 先计算 strs 每个元素中 0 和 1 的个数
const arr = []
for (const str of strs) {
const subArr = [0, 0]
for (const char of str.split('')) {
subArr[char] += 1
}
arr.push(subArr)
}

// 创建三维数组
const dp = new Array(len + 1)
.fill(0)
.map(() => new Array(m + 1).fill(0).map(() => new Array(n + 1).fill(0)))

// 基本的 0-1 背包套路
for (let i = 1; i <= len; i++) {
const [c0, c1] = arr[i - 1]
for (let j = 0; j <= m; j++) {
for (let k = 0; k <= n; k++) {
if (j - c0 < 0 || k - c1 < 0) {
dp[i][j][k] = dp[i - 1][j][k]
} else {
dp[i][j][k] = Math.max(dp[i - 1][j][k], dp[i - 1][j - c0][k - c1] + 1)
}
}
}
}

return dp[len][m][n]
}