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