给你一个二进制字符串数组 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 。
解:动态规划
本题与动态规划方法的01背包模型相似,但背包模型中容量仅用一个变量表示,而本题中,容量则包括0和1的容量两个变量,因此通过一个三维的dp数组来解决。
用dp [ l ] [ i ] [ j ]表示strs中,前 l 个字符串满足1最多 i 个,0最多 j 个的最大子集数。(即前 l 个的最佳组合),则
- 若当前字符串strs[ l ]未被选择,则:dp[ l ] [ i ] [ j ] = dp[ l -1 ] [ i ] [ j ]
- 若选择当前字符串strs[ l ]被选择,则:dp[ l ] [ i ] [ j ] = dp[ l -1 ] [ i-l.oneNum ] [ j-l.ZeroNum ]+1
(其中若满足选择当前字符串,i、j的值必须满足 i >= l.oneNum、j >= l.zeroNum;否则dp[ i ][ j ]的值按照第一条获取。)
最后dp [ n ] [ m ]即结果
示例:
strs:0001 , 0101 , 1000 , 1000;m = 9 , n = 3;
- 初始化dp数组全部为0
- 遍历strs,求每个str[l]对应的dp二维数组:
l = 0:
l = 1:
l = 2:
l = 3:
空间优化:
由于每次求 l+1的dp二维数组时,都是根据 l 的数组进行取值,最后结果也用不上前面的数组,因此只设置滚动数组,取两个数组,一个作为取值的根据,一个用来赋新值。
时间优化:
根据前面分析,当 i < n 和 j < m 时,dp[ l ][ i ] [ j ] = dp[ l-1 ] [ i ] [ j ],因此求 l 的二维数组时,可以直接从i = n;j = m往后更新,不用更新全部数组。
3. 返回dp[ n ][ m ]。
class Solution {
public:
int findMaxForm(vector<string>& strs, int m, int n) {
int zeroNum = 0;
int oneNum = 0;
int i,j;
vector<vector<int>> dpTemp(n+1,vector<int>(m+1,0));
vector<vector<int>> dp(n+1,vector<int>(m+1,0));
for(int l=0 ; l<strs.size() ; l++)
{
zeroNum = count(strs[l].begin() , strs[l].end() , '0');
oneNum = count(strs[l].begin() , strs[l].end() , '1');
for(i = oneNum ; i<n+1 ; i++)
{
for(j=zeroNum ; j<m+1 ; j++)
{
dp[i][j] = max(dpTemp[i][j],dpTemp[i-oneNum][j-zeroNum]+1);
}
}
dpTemp = dp;
}
return dp[n][m];
}
};