474.一和零

86 阅读2分钟

题目:
给你一个二进制字符串数组 strs 和两个整数 m 和 n 。

请你找出并返回 strs 的最大子集的长度,该子集中 最多 有 m 个 0 和 n 个 1 。

如果 x 的所有元素也是 y 的元素,集合 x 是集合 y 的 子集 。

算法:
这种题没法暴力枚举

方法一:动态规划
带2个限制条件的求最大值问题,背包问题的变形。
dp[i][j][k] l:取strs前i个个元素,j个0,k个i,能得到的最大子集个数
状态转移方程: dp[i][j][k] = dp[i - 1][j][k] 第i个元素不取
dp[i][j][k] = dp[i - 1][j][k] 第i个元素取,但是第i个元素的0 or 1的个数超了,取不了
dp[i][j][k] = dp[i - 1][j - zeores][k - ones] + 1 取第i个元素

func findMaxForm(strs []string, m int, n int) int {
	length := len(strs)
	dp := make([][][]int, length + 1)
	for i := range dp {
		dp[i] = make([][]int, m + 1)
		for j := range dp[i] {
			dp[i][j] = make([]int, n + 1)
		}
	}
        // 要有一个初始条件i=0的dp[i][j][k]全部为0,从1开始遍历strs
	for i := 1; i <= length; i ++ {
		for j := 0; j <= m; j ++ {
			for k := 0; k <= n ; k ++ {
				dp[i][j][k] = dp[i - 1][j][k]
				items := countOneZero(strs[i - 1])
				zeros, ones := items[0], items[1]
				if j >= zeros && k >= ones {
					dp[i][j][k] = max(dp[i][j][k], dp[i - 1][j - zeros][k - ones] + 1)
				}
			}
		}
	}

	return dp[length][m][n]
}

func max(a, b int) int {
	if a > b {
		return a
	}
	return b
}
func countOneZero(str string) []int {
	items := make([]int, 2)
	for i := range str {
		items[str[i] - '0'] ++
	}
	return items
}

方法二:动态规划空间优化
dp[i]只和dp[i-1]有关,所以可以压缩空间,减少一维空间。

func findMaxForm(strs []string, m int, n int) int {
	dp := make([][]int, m+1)
	for i := range dp {
		dp[i] = make([]int, n+1)
	}

	for i := range strs {
		items := countOneZero(strs[i])
		zeros, ones := items[0], items[1]
		for j := m; j >= zeros; j-- {
			for k := n; k >= ones; k--{
				dp[j][k] = max(dp[j][k], dp[j-zeros][k-ones]+1)
			}
		}
	}

	return dp[m][n]
}

func max(a, b int) int {
	if a > b {
		return a
	}
	return b
}
func countOneZero(str string) []int {
	items := make([]int, 2)
	for i := range str {
		items[str[i]-'0']++
	}
	return items
}