In the computer world, use restricted resource you have to generate maximum benefit is what we always want to pursue.
For now, suppose you are a dominator(支配者、统治者) of m 0s
and n 1s
respectively. On the other hand, there is an array with strings consisting of only 0s
and 1s
.
Now your task is to find the maximum number of strings that you can form with given m 0s
and n 1s
. Each 0
and 1
can be used at most once.
Note:
- The given numbers of
0s
and1s
will both not exceed100
- The size of given string array won't exceed
600
.
Example 1:
Input: Array = {"10", "0001", "111001", "1", "0"}, m = 5, n = 3 Output: 4 Explanation: This are totally 4 strings can be formed by the using of 5 0s and 3 1s, which are “10,”0001”,”1”,”0”
Example 2:
Input: Array = {"10", "0", "1"}, m = 1, n = 1 Output: 2 Explanation: You could form "10", but then you'd have nothing left. Better form "0" and "1".
public int findMaxForm(String[] strs, int m, int n) {
int[][] str01Count=new int[strs.length][2];
for(int i=0;i<strs.length;i++){
char[] cs=strs[i].toCharArray();
int count0=0;int count1=0;
for(int j=0;j<cs.length;j++){
if(cs[j]=='0'){
count0++;
}
else {
count1++;
}
}
str01Count[i]=new int[]{count0,count1};
}
Integer[][][] memo=new Integer[strs.length][m+1][n+1];
return helper(str01Count, 0, m, n, memo);
}
public int helper(int[][] str01Count,int index,int m,int n,
Integer[][][] memo){
if(index>=str01Count.length){
return 0;
}
if(memo[index][m][n]!=null){
return memo[index][m][n];
}
int maxCount=0;
int count1=helper(str01Count, index+1, m, n,memo);
int count2=0;
if(m>=str01Count[index][0]&&n>=str01Count[index][1]){
count2=1+helper(str01Count, index+1, m-str01Count[index][0], n-str01Count[index][1],memo);
}
maxCount=Math.max(count1, count2);
memo[index][m][n]=maxCount;
return maxCount;
}
大神说这是个典型的0/1背包问题,用DP做。
构造三维数组:dp[i][j][k] 表示:限制使用 j 个0,k个 1,从 0~i 范围内的strs 能构造最多几个字符串。
对于 dp[i][j][k], 当前判断的字符串是 strs[i], 我们要么取它,要么抛弃它。因此 dp[ i ][ j ][ k ] = dp[ i-1 ][ j-numOf0(strs[i]) ][ i-numOf1(strs[i]) ] 或者dp[ i ][ j ][ k ] = dp[ i-1 ][ j ][ k ]; 我们只需要取两者中较大的那个,来赋值给 dp[i][j][k]。
public int findMaxForm(String[] strs, int m, int n) {
int l = strs.length;
int[][][] dp = new int[l+1][m+1][n+1];
for (int i = 1; i <= l; i++) {
int[] nums = new int[]{0,0};
nums = calculate(strs[i-1]);
for (int j = 0; j < m+1; j++) {
for (int k = 0; k < n+1; k++) {
if (j>=nums[0] && k>=nums[1]) {
dp[i][j][k] = Math.max(dp[i-1][j][k], dp[i-1][j-nums[0]][k-nums[1]]+1);
} else {
dp[i][j][k] = dp[i-1][j][k];
}
}
}
}
return dp[l][m][n];
}
private int[] calculate(String str) {
int[] res = new int[2];
for (char ch : str.toCharArray()) {
if (ch == '0') {
res[0]++;
} else if (ch == '1') {
res[1]++;
}
}
return res;
}
背包问题我们无法减少时间复杂度,但是可以减少空间复杂度,从 ijk 减少到 j*k 。
对于每个 str
, 假定它有 a
个"0"
s 和 b
个"1"
s。关系式为 dp[i][j] = Math.max(dp[i][j], dp[i - a][j - b] + 1)
。
public int findMaxForm(String[] strs, int m, int n) {
int l = strs.length;
int[][] dp = new int[m+1][n+1];
int[] nums = new int[]{0,0};
for (String str : strs) {
nums = calculate(str);
for (int j = m; j >= nums[0]; j--) {
for (int k = n; k >= nums[1]; k--) {
if (j>=nums[0] && k>=nums[1]) {
dp[j][k] = Math.max(dp[j][k], dp[j-nums[0]][k-nums[1]]+1);
} else {
dp[j][k] = dp[j][k];
}
}
}
}
return dp[m][n];
}
private int[] calculate(String str) {
int[] res = new int[2];
for (char ch : str.toCharArray()) {
if (ch == '0') {
res[0]++;
} else if (ch == '1') {
res[1]++;
}
}
return res;
}
If you know Chinese, http://love-oriented.com/pack/P01.html would help you a lot.