题目地址:
https://leetcode.com/problems/ones-and-zeroes/
给定一个长 n n n字符串数组 A A A,里面的字符串只含 0 0 0和 1 1 1。再给定两个正整数 u u u和 v v v,要求取出最多的字符串,使得 0 0 0的总数不超过 u u u并且 1 1 1的总数不超过 v v v,问最多能取多少个字符串。
思路是动态规划。设 f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k]是只从前 i i i个字符串里选的话,要求 0 0 0的总数不超过 j j j并且 1 1 1的总数不超过 k k k的情况下,能选出的最多字符串个数。初始条件 f [ 0 ] f[0] f[0]都是 0 0 0,设第 i i i个字符串有 x x x个 0 0 0和 y y y个 1 1 1,可以按照第 i i i个字符串选不选来分类,则有: f [ i ] [ j ] [ k ] = max { f [ i − 1 ] [ j ] [ k ] , 1 + f [ i − 1 ] [ j − x ] [ k − y ] } f[i][j][k]=\max\{f[i-1][j][k],1+f[i-1][j-x][k-y]\} f[i][j][k]=max{f[i−1][j][k],1+f[i−1][j−x][k−y]}代码里可以用滚动数组优化,但 j j j和 k k k的两维要从大到小更新。代码如下:
public class Solution {
public int findMaxForm(String[] strs, int m, int n) {
int len = strs.length;
int[][] dp = new int[m + 1][n + 1];
int[] cost0 = new int[len], cost1 = new int[len];
for (int i = 0; i < len; i++) {
String s = strs[i];
for (int j = 0; j < s.length(); j++) {
if (s.charAt(j) == '0') {
cost0[i]++;
} else {
cost1[i]++;
}
}
}
for (int i = 1; i <= len; i++) {
for (int j = m; j >= cost0[i - 1]; j--) {
for (int k = n; k >= cost1[i - 1]; k--) {
dp[j][k] = Math.max(dp[j][k], 1 + dp[j - cost0[i - 1]][k - cost1[i - 1]]);
}
}
}
return dp[m][n];
}
}
时间复杂度 O ( n u v ) O(nuv) O(nuv),空间 O ( u v ) O(uv) O(uv)。
C++:
class Solution {
public:
int findMaxForm(vector<string>& strs, int m, int n) {
vector<vector<int>> f(m + 1, vector<int>(n + 1));
for (auto& s : strs) {
int n0 = 0, n1 = 0;
for (auto& ch : s)
if (ch == '0') n0++;
else n1++;
for (int i = m; i >= n0; i--)
for (int j = n; j >= n1; j--)
f[i][j] = max(f[i][j], f[i - n0][j - n1] + 1);
}
return f[m][n];
}
};
时空复杂度一样。