其他分享
首页 > 其他分享> > 【动态规划】力扣474:一和零()

【动态规划】力扣474:一和零()

作者:互联网

给你一个二进制字符串数组 strs 和两个整数 m 和 n 。
请你找出并返回 strs 的最大子集的长度,该子集中 最多 有 m 个 0 和 n 个 1 。
如果 x 的所有元素也是 y 的元素,集合 x 是集合 y 的 子集 。
示例:

输入: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 。

这道题和经典的背包问题非常相似,但是和经典的背包问题只有一种容量不同,这道题有两种容量,即选取的字符串子集中的 0 和 1 的数量上限。
经典的背包问题可以使用二维动态规划求解,两个维度分别是物品和容量。这道题有两种容量,因此需要使用三维动态规划求解,三个维度分别是字符串、0 的容量和 1 的容量。
定义三维数组 dp,其中 dp[i][j][k] 表示在前 i 个字符串中,使用 j 个 0 和 k 个 1 的情况下最多可以得到的字符串数量。假设数组 str 的长度为 l,则最终答案为 dp[l][m][n]。
当没有任何字符串可以使用时,可以得到的字符串数量只能是 0,因此动态规划的边界条件是:当 i=0 时,对任意 0≤j≤m 和 0≤k≤n,都有 dp[i][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 的容量分别是 j 和 k 时,考虑以下两种情况:dp[i][j][k] = dp[i−1][j][k];
如果 j≥zeros 且 k≥ones,则如果不选第 i 个字符串,有 dp[i][j][k] = dp[i−1][j][k],如果选第 i 个字符串,有 dp[i][j][k]=dp[i−1][j−zeros][k−ones]+1,dp[i][j][k] 的值应取上面两项中的最大值。
因此状态转移方程如下:
image

class Solution:
    def findMaxForm(self, strs: List[str], m: int, n: int) -> int:

        length = len(strs)
        dp = [[[0] * (n+1) for _ in range(m+1)] for _ in range(length+1)]

        for i in range(1, length+1):
            c0 = strs[i-1].count('0')       # 当前字符串中0的数目
            c1 = len(strs[i-1]) - c0        # 当前字符串中1的数目
            for j in range(m+1):            # 第二层循环:0的背包容量
                for k in range(n+1):        # 第三层循环:1的背包容量
                    if j < c0 or k < c1:    # 无法添加当前字符串
                        dp[i][j][k] = dp[i-1][j][k]
                    else:                   # 可选或不选当前字符串,取两者之间的较大值
                        dp[i][j][k] = max( dp[i-1][j][k], dp[i-1][j-c0][k-c1] + 1 )

        return dp[length][m][n]

作者:flix
链接:https://leetcode-cn.com/problems/ones-and-zeroes/solution/by-flix-a384/

时间复杂度:O(l⋅(m⋅n+M)),其中 l 是字符串数组 strs 的长度,M 是每个字符串的平均长度。
空间复杂度:O(l⋅m⋅n)。

由于 dp[i][][] 的每个元素值的计算只和 dp[i−1][][] 的元素值有关,因此可以使用滚动数组的方式,去掉 dp 的第一个维度,将空间复杂度优化到 O(mn)。
实现时,内层循环需采用倒序遍历的方式,这种方式保证转移来的是 dp[i−1][][] 中的元素值。状态转移方程为:
dp[j][k]=max{dp[j][k],dp[j−zeros][k−ones]+1}

class Solution:
    def findMaxForm(self, strs: List[str], m: int, n: int) -> int:

        length = len(strs)
        dp = [[0] * (n+1) for _ in range(m+1)]

        for s in strs:                              # 枚举字符串
            c0 = s.count('0')                       # 当前字符串中0的数目
            c1 = len(s) - c0                        # 当前字符串中1的数目
            for j in range(m, c0-1, -1):            # 第二层循环倒序:0的背包容量
                for k in range(n, c1-1, -1):        # 第三层循环倒序:1的背包容量
                    if j < c0 or k < c1:            # 无法添加当前字符串
                        dp[j][k] = dp[j][k]
                    else:                   # 可选或不选当前字符串,取两者之间的较大值
                        dp[j][k] = max( dp[j][k], dp[j-c0][k-c1] + 1 )

        return dp[m][n]

作者:flix
链接:https://leetcode-cn.com/problems/ones-and-zeroes/solution/by-flix-a384/

时间复杂度:O(lmn+L),其中 l 是数组 strs 的长度,m 和 n 分别是 0 和 1 的容量,L 是数组 strs 中的所有字符串的长度之和。动态规划需要计算的状态总数是 O(lmn),每个状态的值需要 O(1) 的时间计算。对于数组 strs 中的每个字符串,都要遍历字符串得到其中的 0 和 1 的数量,因此需要 O(L) 的时间遍历所有的字符串。总时间复杂度是 O(lmn+L)。
空间复杂度:O(mn),其中 m 和 n 分别是 0 和 1 的容量。使用空间优化的实现,需要创建 m+1 行 n+1 列的二维数组 dp。

标签:c0,容量,strs,力扣,range,474,字符串,动态,dp
来源: https://www.cnblogs.com/Jojo-L/p/16191537.html