一和零

给你一个二进制字符串数组 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 。

提示:

1 <= strs.length <= 600
1 <= strs[i].length <= 100
strs[i] 仅由 '0' 和 '1' 组成
1 <= m, n <= 100

题目理解失败:还以为是数组中每个元素的01个数与m和n的比较。

func findMaxForm(strs []string, m int, n int) int {

	result := 0
	for i:=0;i<len(strs);i++{
		m0 := strings.Count(strs[i],"0")
		n1 := strings.Count(strs[i],"1")
		if m0+n1<m+n&&m0<=m&&n1<=n{
			result++
		}
	}
	return result
}

正确理解:数组中由各个元素组成的01个数不大于m和n的组合。

例子:
strs = ["10", "0001", "111001", "1", "0"], m = 5, n = 3
最大的集合为:{"10","0001","1","0"}。这里0的个数为5,1的个数为3。并且也是最大的组合。

贪心算法:思路错误

将数组中的元素,按照元素的长度进行重新排序。然后从长度最小进行匹配,直到遍历结束或者m和n都等于0。但是当元素的长度都相等的时候,思路错误。

func findMaxForm(strs []string, m int, n int) int {
	sort.Strings(strs)
	head0 := []string{}
	head1 := []string{}
	for i:=0;i<len(strs);i++{
		if string(strs[i][0])=="0" {
			head0 = append(head0,strs[i])
		}else if string(strs[i][0])=="1" {
			head1 = append(head1,strs[i])
		}
	}
	// 归并
	x,y := 0,0
	tmp := []string{}
	for x<len(head0)&&y<len(head1) {
		if len(head0[x]) <= len(head1[y]) {
			tmp = append(tmp,head0[x])
			x++
		}else if len(head0[x]) > len(head1[y]) {
			tmp = append(tmp,head1[y])
			y++
		}
	}
	if x<len(head0) {
		tmp = append(tmp,head0[x:]...)
	}
	if y<len(head1) {
		tmp = append(tmp,head1[y:]...)
	}
	log.Println(tmp)
	// 从这里开始执行贪心算法,直接从长度最小的开始算起,直到满足条件
	result := 0
	for i:=0;i<len(tmp);i++{
		m0 := strings.Count(tmp[i],"0")
		n1 := strings.Count(tmp[i],"1")
		if m0<=m&&n1<=n {
			result++
			m=m-m0
			n=n-n1
		}
	}
	return result
}

正确做法

这道题跟经典的背包问题相似。背包问题使用的是动态规划的解法,并且使用的是二维数组,两个维度分别表示为物品和容量。这道题的容量为0和1的个数,有两个,因此需要用三维数组表示。其中三维数组的维度分别代表为:字符串、0的容量和1的容量。并且三维数组的每个元素的代表,即dp[i]/[j]/[k]代表为:有i个字符串、j个0和k个1的情况下最多可以得到的字符串数量。假设数组长度为l,则最终答案为dp[l]/[j]/[k]。

当没有字符串的时候,可以得到dp[0]/[j]/[k]的值都为0。当1<=i<=l的时候,对于strs中的第i个字符串(计数从1开始),首先遍历该字符串0的个数和1的个数,分别记为:zeros和ones,然后0<=j<=m和0<=k<=n,计算dp[i]/[j]/[k]的值。

当0和1的容量分别是i和j的时候,考虑以下两种情况:

  • 如果j<zeros和k<ones时,则不能选择这个字符串,此时有dp[i]/[j]/[k]=dp[i−1]/[j]/[k]。
  • 如果j>=zeros和k>ones时,则如果不选择这个字符串,此时有dp[i]/[j]/[k]=dp[i−1]/[j]/[k]。如果选择这个字符串,有dp[i]/[j]/[k]=dp[i-1]/[j-zeros]/[k-ones]+1。因此对于这两种情况的选择,根据题意选择最大的为当前值。

实现代码

// m代表0的个数,n代表1的个数
func findMaxForm(strs []string, m int, n int) int {

	// 背包问题的转化而已,只不过容量有两个而已
	length := len(strs)
	dp := make([][][]int,length+1)   // 为什么不是length呢?因为要取到dp[length]的下标
	for i:=range dp{
		dp[i] = make([][]int,m+1)    // 同上
		for j:=range dp[i] {
			dp[i][j] = make([]int,n+1)  // 同上
		}
	}

	for i,s := range strs{    // i的表示字符个数
		zeros := strings.Count(s,"0")
		ones := len(s)-zeros
		for j:=0;j<=m;j++{    // 表示有多少个0
			for k:=0;k<=n;k++{  // 表示有多少个1
				dp[i+1][j][k] = dp[i][j][k]
				if j>=zeros&&k>=ones {
					dp[i+1][j][k] = max(dp[i+1][j][k],dp[i][j-zeros][k-ones]+1)  // 表示取第i个字符串的时候,总共得到的字符串个数
				}
			}
		}
	}
	return dp[length][m][n]
}


func max(x,y int)int{
	if x>y {
		return x
	}
	return y
}
原文地址:https://www.cnblogs.com/MyUniverse/p/14855061.html