题目来源:https://leetcode.cn/problems/ones-and-zeroes/description/
C++题解:本题其实是01背包问题!只不过这个背包有两个维度,一个是m 一个是n,而不同长度的字符串就是不同大小的待装物品。动规五部曲:
- 确定dp数组(dp table)以及下标的含义。dp[i][j]:最多有i个0和j个1的strs的最大子集的大小为dp[i][j]。
- 确定递推公式。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);
- dp数组如何初始化。因为物品价值不会是负数,初始为0,保证递推的时候dp[i][j]不会被初始值覆盖。
- 确定遍历顺序。01背包外层for循环遍历物品,内层for循环遍历背包容量且从后向前遍历!本题也是,物品就是strs里的字符串,背包容量就是题目描述中的m和n。
- 举例推导dp数组。
class Solution {
public:int findMaxForm(vector<string>& strs, int m, int n) {int len = strs.size();// strs每个元素的01个数情况vector<int> zs(len, 0), os(len, 0);for(int i = 0; i < len; i++){int chang = strs[i].size();for(int j = 0; j < chang; j++){if(strs[i][j] == '0') zs[i]++;else os[i]++;}}// 二维动态数组,从后往前遍历// dp[i][j]:最多有i个0和j个1的strs的最大子集的大小为dp[i][j]。vector<vector<int>> dp(m+1, vector<int>(n+1, 0));for(int i = 0; i < len; i++) {for(int j = m; j >= zs[i]; j--) {for(int k = n; k >= os[i]; k--) {dp[j][k] = max(dp[j][k], dp[j-zs[i]][k-os[i]] + 1);}}}return dp[m][n];}
};
代码随想录代码:
class Solution {
public:int findMaxForm(vector<string>& strs, int m, int n) {vector<vector<int>> dp(m + 1, vector<int> (n + 1, 0)); // 默认初始化0for (string str : strs) { // 遍历物品int oneNum = 0, zeroNum = 0;for (char c : str) {if (c == '0') zeroNum++;else oneNum++;}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];}
};