力扣:474. 一和零(动态规划)(01背包)

题目:

给你一个二进制字符串数组 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 <= 600
  • 1 <= strsi.length <= 100
  • strsi 仅由 '0' 和'1' 组成
  • 1 <= m, n <= 100

思路:

本题是01背包问题

只不过这个背包有两个维度,一个是m 一个是n,而不同长度的字符串就是不同大小的待装物品。

动态规划五部曲:

  1. 确定dp数组(dp table)以及下标的含义

dpij最多有i个0和j个1的strs的最大子集的大小为dpij

  1. 确定递推公式

dpij 可以由前一个strs里的字符串推导出来,strs里的字符串有zeroNum个0,oneNum个1。

dpij 就可以是 dpi - zeroNumj - oneNum + 1。

然后我们在遍历的过程中,取dpij的最大值。

所以递推公式:dpij = max(dpij, dpi - zeroNumj - oneNum + 1);

此时大家可以回想一下01背包的递推公式:dpj = max(dpj, dpj - weight\[i] + valuei);

对比一下就会发现,字符串的zeroNum和oneNum相当于物品的重量(weighti),字符串本身的个数相当于物品的价值(valuei)。

这就是一个典型的01背包! 只不过物品的重量有了两个维度而已。

python 复制代码
dp[i][j] = max(dp[i][j], dp[i - zero_num][j - one_num] + 1)
  1. dp数组如何初始化

01背包的dp数组初始化为0就可以。

因为物品价值不会是负数,初始为0,保证递推的时候dpij不会被初始值覆盖。

  1. 确定遍历顺序

01背包一定是外层for循环遍历物品,内层for循环遍历背包容量且从后向前遍历!

那么本题也是,物品就是strs里的字符串,背包容量就是题目描述中的m和n。

代码如下:

python 复制代码
            # 遍历m到zero_num,更新dp数组
            for i in range(m, zero_num - 1, -1):
                # 遍历n到one_num,更新dp数组
                for j in range(n, one_num - 1, -1):
                    # 更新dp[i][j]的值
                    dp[i][j] = max(dp[i][j], dp[i - zero_num][j - one_num] + 1)

m 和 n都是物品重量的一个维度,先遍历哪个都可以。

  1. 举例推导dp数组
    以输入:"10","0001","111001","1","0",m = 3,n = 3为例

最后dp数组的状态如下所示:

代码及详细注释:

python 复制代码
class Solution:
    def findMaxForm(self, strs: List[str], m: int, n: int) -> int:
        # 创建一个二维数组dp,用于记录可以由前i个字符串组成的最大子集的个数
        dp = [[0] * (n + 1) for _ in range(m + 1)]
        # 遍历每个字符串
        for s in strs:
            zero_num = s.count('0')  # 统计0的个数
            one_num = s.count('1')  # 统计1的个数
            # 遍历m到zero_num,更新dp数组
            for i in range(m, zero_num - 1, -1):
                # 遍历n到one_num,更新dp数组
                for j in range(n, one_num - 1, -1):
                    # 更新dp[i][j]的值
                    dp[i][j] = max(dp[i][j], dp[i - zero_num][j - one_num] + 1)
        # 返回dp[m][n],表示可以由给定数量的0和1组成的最大子集的个数
        return dp[m][n]
  • 时间复杂度: O(kmn),k 为strs的长度
  • 空间复杂度: O(mn)
相关推荐
致Great3 小时前
Pi 的上下文压缩,到底是怎么工作的?
算法
CTA量化套保4 小时前
近期零基础量化学习:先分阶段,再用 AI 检查缺口
人工智能·python
watersink4 小时前
机器学习聚类算法
算法·机器学习·聚类
清水白石0085 小时前
Python 死锁排查全攻略:从线程卡死到锁依赖定位与工程化修复
linux·网络·python
Elias不吃糖5 小时前
Langfuse 入门:Trace、Prompt、Dataset、Experiment、Evaluator
前端·python·prompt·langfuse
luj_17685 小时前
桥牌思维启示:系统设计的模块化架构
c语言·开发语言·c++·经验分享·算法
TechWayfarer5 小时前
批量查IP归属地,在线API、脚本、离线库怎么选?三种方案实测对比
网络·python·网络协议·tcp/ip
七牛开发者7 小时前
为什么 Go 很适合 AI 辅助开发?
数据库·人工智能·python·elasticsearch·log4j
河南凹凸环境艺术设计7 小时前
性价比高的民宿酒店设计企业
大数据·人工智能·python