474. 一和零
给你一个二进制字符串数组 strs 和两个整数 m 和 n 。
请你找出并返回 strs 的最大子集的大小,该子集中 最多 有 m 个 0 和 n 个 1 。
如果 x 的所有元素也是 y 的元素,集合 x 是集合 y 的 子集 。
示例 1:
输入:strs = ["10", "0001", "111001", "1", "0"], m = 5, n = 3
输出:4
解释:最多有 5 个 0 和 3 个 1 的最大子集是 {"10","0001","1","0"} ,因此答案是 4 。
其他满足题意但较小的子集包括 {"0001","1"} 和 {"10","1","0"} 。{"111001"} 不满足题意,因为它含 4 个 1 ,大于 n 的值 3 。
示例 2:
输入:strs = ["10", "0", "1"], m = 1, n = 1
输出:2
解释:最大的子集是 {"0", "1"} ,所以答案是 2 。
提示:
1 <= strs.length <= 6001 <= strs[i].length <= 100strs[i]仅由'0'和'1'组成1 <= m, n <= 100
中等 动态规划
题解
这道题和经典的背包问题非常相似,但是和经典的背包问题只有一种容量不同,这道题有两种容量,即选取的字符串子集中的 0 和 1 的数量上限。
经典的背包问题可以使用二维动态规划求解,两个维度分别是物品和容量。这道题有两种容量,因此需要使用三维动态规划求解,三个维度分别是字符串、0 的容量和 1 的容量。
定义三维数组 dp,其中 dp[i][j][k] 表示在前 i 个字符串中,使用 j 个 0 和 k 个 1 的情况下最多可以得到的字符串数量。假设数组 str 的长度为 l,则最终答案为 dp[l][m][n]。
代码
1. 三维dp
class Solution:
def findMaxForm(self, strs: List[str], m: int, n: int) -> int:
Len = len(strs)
dp = [[[0 for _ in range(n + 1)] for _ in range(m + 1)] for _ in range(Len + 1)]
for k in range(1, Len + 1):
cnt0 = strs[k-1].count('0')
cnt1 = strs[k-1].count('1')
for i in range(m + 1):
for j in range(n + 1):
dp[k][i][j] = dp[k-1][i][j] #继承
if i - cnt0 >= 0 and j - cnt1 >= 0: #可更新则更新
dp[k][i][j] = max(dp[k][i][j], dp[k-1][i-cnt0][j-cnt1] + 1)
return dp[Len][m][n]
2. 二维dp
可以把外循环去掉
但是要注意循环的顺序就好和0-1背包问题一样,逆序 确保优化时取的值为旧值
class Solution:
def findMaxForm(self, strs: List[str], m: int, n: int) -> int:
dp = [[0 for _ in range(n + 1)] for _ in range(m + 1)]
for s in strs:
cnt0 = s.count('0')
cnt1 = s.count('1')
for i in range(m, cnt0 - 1, -1): #0-1背包问题,内循环逆序
for j in range(n, cnt1 - 1, -1):
dp[i][j] = max(dp[i][j], dp[i-cnt0][j-cnt1] + 1)
return dp[m][n]