题目
中等
相关标签
给你一个二进制字符串数组 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 <= strs[i].length <= 100
strs[i]
仅由'0'
和'1'
组成1 <= m, n <= 100
思路和解题方法
- 定义一个二维动态规划数组
dp
,其中dp[i][j]
表示当背包容量为i
个 0 和j
个 1 时,所能选择的字符串的最大数量。- 对于每个字符串
str
,统计其中 0 和 1 的数量,分别记为zeroNum
和oneNum
。- 使用双重循环遍历背包容量
i
和j
,从大到小遍历,以确保在更新dp[i][j]
时使用的是上一轮循环中的值。具体地,在第二重循环中,对于当前的i
和j
,有两种选择:
- 不选择当前字符串,此时
dp[i][j]
不变;- 选择当前字符串,此时需要将
i
减去zeroNum
,将j
减去oneNum
,并将dp[i-zeroNum][j-oneNum]
加上 1,表示选择当前字符串后的最大数量。- 在双重循环结束后,
dp[m][n]
即为所求。
复杂度
时间复杂度:
O(k*m*n)
时间复杂度:该算法使用了二维动态规划数组,对于每个字符串需要遍历一次整个数组,因此时间复杂度为 O(k * m * n),其中 k = len(strs) 表示字符串数组的长度。
空间复杂度
O(m*n)
空间复杂度:该算法使用了一个二维动态规划数组,因此空间复杂度为 O(m * n)。需要注意的是,在实际代码中,我们可以将二维数组优化为一维数组,从而将空间复杂度降低到 O(n)。
c++ 代码
cpp
class Solution {
public:
int findMaxForm(vector<string>& strs, int m, int n) {
// 创建动态规划数组 dp,大小为 (m+1) x (n+1),并将所有元素初始化为 0
vector<vector<int>> dp (m+1,vector<int> (n+1,0));
// 遍历字符串数组 strs
for(string str :strs)
{
// 统计当前字符串中 0 和 1 的个数
int oneNum = 0,zeroNum = 0;
for(char c:str) if(c == '0') zeroNum++; else oneNum++;
// 使用双重循环遍历动态规划数组 dp
for(int i = m;i>=zeroNum;i--) // 遍历背包容量 m
for(int j = n;j>=oneNum;j--) // 遍历背包容量 n
{
// 如果当前字符串可以被放入背包中,更新 dp[i][j] 的值为 dp[i-zeroNum][j-oneNum] + 1
// 表示容量为 i、j 的背包所能装载的最大字符串数量
dp[i][j] = max(dp[i][j],dp[i-zeroNum][j-oneNum] + 1);
}
}
// 返回容量为 m、n 的背包所能装载的最大字符串数量
return dp[m][n];
}
};
觉得有用的话可以点点赞,支持一下。
如果愿意的话关注一下。会对你有更多的帮助。
每天都会不定时更新哦 >人< 。