给你一个二进制字符串数组 strs 和两个整数 m 和 n 。
请你找出并返回 strs 的最大子集的长度,该子集中 最多 有 m 个 0 和 n 个 1 。
如果 x 的所有元素也是 y 的元素,集合 x 是集合 y 的 子集 。
思路:背包有两个维度:m、n。不同长度的字符串就是不同大小的待装物品。
动规五部曲
1、确定dp[i][j]的下标含义及其定义:最多有i个0和j个1的strs的最大子集的大小为dp[i][j]。
2、递推关系式:dp[i][j] 可以由前一个strs里的字符串推导出来,strs里的字符串有zeroNum个0,oneNum个1。dp[i][j] 就可以是 dp[i - zeroNum][j - oneNum] + 1。然后我们在遍历的过程中,取dp[i][j]的最大值。所以递推公式:dp[i][j] = max(dp[i][j], dp[i - zeroNum][j - oneNum] + 1);
此时大家可以回想一下01背包的递推公式:dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
对比一下就会发现,字符串的zeroNum和oneNum相当于物品的重量(weight[i]),字符串本身的个数相当于物品的价值(value[i])。
这就是一个典型的01背包! 只不过物品的重量有了两个维度而已。
3、初始化:因为物品价值不会是负数,初始为0,保证递推的时候dp[i][j]不会被初始值覆盖。
4、确定遍历顺序:先物品,再背包,倒序。
5、举例推导dp数组。
class Solution {
public:
int findMaxForm(vector<string>& strs, int m, int n) {
//初始化
vector<vector<int>> dp(m+1,vector<int>(n+1,0));
//遍历顺序,先物品再背包
for(string str : strs)
{
//获取每个str有多少个0和1
int oneNum=0,zeroNum=0;
for(char c : str)
{
if(c=='1') oneNum++;
if(c=='0') zeroNum++;
}
for(int i=m;i>=zeroNum;i--)
{
for(int j=n;j>=oneNum;j--)
{
dp[i][j]=max(dp[i][j],dp[i-zeroNum][j-oneNum]+1);
}
}
}
return dp[m][n];
}
};