首页 > 其他分享 >力扣-474. 一和零

力扣-474. 一和零

时间:2024-05-30 09:48:01浏览次数:20  
标签:count int 力扣 strs vector 474 字符串 dp

1.题目

题目地址(474. 一和零 - 力扣(LeetCode))

https://leetcode.cn/problems/ones-and-zeroes/

题目描述

给你一个二进制字符串数组 strs 和两个整数 mn

请你找出并返回 strs 的最大子集的长度,该子集中 最多m0n1

如果 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

2.题解

2.1 三维dp数组

思路

这里设计一个三维dp数组dp[i][j][k], 其中i表示在[0,i-1]中选择任意个数, j表示最多容忍的0的个数,k表示最多容忍的1的个数;而dp的值则记录已经选择了字符串的最大可能个数!
对于每一个字符串,我们转换为0-1背包问题,可以选择 选 或者 不选:
1.不选的话就是dp[i][j][k] = dp[i-1][j][k], 从之前的进行继承
2.选的话就是:
2.1 如果 j > zeros, k > ones, 说明最大容忍个数允许这个字符串作为子集的一份子加入,
所以我们dp[i][j][k] = max(dp[i][j][k], dp[i - 1][j - zeros][k - ones] + 1);, 保留原值和更新值(加了一个字符串,不要忘记+1)中的较大值即可
2.2 反之我们根本无法加入该字符串到当前子集中,所以无需考虑,跳过即可

代码

  • 语言支持:C++

C++ Code:

class Solution {
public:
    // 计算当前字符串中0和1的数量
    vector<int> getZerosOnes(string& str) {
        vector<int> count(2);
        for (char ch : str) {
            count[ch - '0']++;
        }
        return count;
    }

    int findMaxForm(vector<string>& strs, int m, int n) {
        int len = strs.size();
        // dp数组记录当前[0,i-1]范围中,0个数为j,1个数为k情况下选择的字符串个数
        vector<vector<vector<int>>> dp(len + 1, vector<vector<int>>(m + 1, vector<int>(n + 1, 0)));

        // 更新dp数组(从1开始,之前已有隐式初始化所有i=0的值为0)
        for (int i = 1; i <= len; i++) {
            string str = strs[i - 1]; // 这里strs索引依旧是从0开始,所以不要忘记-1
            vector<int> count = getZerosOnes(str);
            int zeros = count[0], ones = count[1];
            for (int j = 0; j <= m; j++) {
                for (int k = 0; k <= n; k++) {
                    // 如果不选择该字符串,保留原字符串个数
                    dp[i][j][k] = dp[i - 1][j][k];
                    // 如果选择字符串,可能遇到更新的dp已经存在值
                    // 这时候我们选取字符串个数多的保留即可(注意这里选择了该字符串,不要忘记了+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[len][m][n];
    }
};

复杂度分析

令 n 为数组长度。

  • 时间复杂度:\(O(lmn+L)\)
    其中\(l\)是数组strs的长度,\(m\)和\(n\)分别是 0 和 1 的容量,\(L\)是数组strs中的所有字符串的长度之和。
    动态规划需要计算的状态总数是\(O(lmn)\),每个状态的值需要\(O(1)\)的时间计算。
    对于数组strs中的每个字符串,都要遍历字符串得到其中的0 和 1 的数量,因此需要\(O(L)\)的时间遍历所有的字符串。
    总时间复杂度是\(O(lmn+L)\) 。
  • 空间复杂度:\(O(lmn)\)

2.2 二维dp数组(空间优化)

思路

同416-分割等和子集,我们这里发现其实对于dp[i]... = dp[i-1]..., 我们每次只需要记录上一次的dp值即可,而不要记录每一个i对应的dp值
所以这里我们只需要dp[j][k]即可解决问题。

但是这里我们需要注意一个问题:如果我们正序遍历,由于dp[j][k] = dp[j-zeros][k-ones]的存在,这里更大的序列值优先被更新;
等我们遍历到这些较大序列dp,会发现它的值已经不是上一次存储的值,而在之前已经被更新了;
为了避免这种情况,我们选择倒序遍历,便可以完美解决问题!

代码

class Solution {
public:
    // 计算当前字符串中0和1的数量
    vector<int> getZerosOnes(string& str) {
        vector<int> count(2);
        for (char ch : str) {
            count[ch - '0']++;
        }
        return count;
    }

    int findMaxForm(vector<string>& strs, int m, int n) {
        int len = strs.size();
        vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0));

        // 更新dp数组(从1开始,之前已有隐式初始化所有i=0的值为0)
        for (int i = 1; i <= len; i++) {
            string str = strs[i - 1]; // 这里strs索引依旧是从0开始,所以不要忘记-1
            vector<int> count = getZerosOnes(str);
            int zeros = count[0], ones = count[1];
            for (int j = m; j >= 0; j--) {
                for (int k = n; k >= 0; k--) {
                    // 如果选择字符串,可能遇到更新的dp已经存在值
                    // 这时候我们选取字符串个数多的保留即可(注意这里选择了该字符串,不要忘记了+1)
                    if (j >= zeros && k >= ones) {
                        dp[j][k] = max(dp[j][k], dp[j - zeros][k - ones] + 1);
                    }
                }
            }
        }
        return dp[m][n];
    }
};

复杂度分析

令 n 为数组长度。

  • 时间复杂度:\(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)\)

标签:count,int,力扣,strs,vector,474,字符串,dp
From: https://www.cnblogs.com/trmbh12/p/18221616

相关文章

  • P9 【力扣+知识点】【算法】【二分查找】C++版
    【704】二分查找(模板题)看到复杂度logN,得想到二分给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target  ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。示例1:输入:nums=[-1,0,3,5,9,12],target=9输出:4解释:9出现在......
  • 力扣-416. 分割等和子集
    1.题目题目地址(416.分割等和子集-力扣(LeetCode))https://leetcode.cn/problems/partition-equal-subset-sum/题目描述给你一个只包含正整数的非空数组 nums。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。 示例1:输入:nums=[1,5,11,5]......
  • 力扣算法之1050. 合作过至少三次的演员和导演
    题解actor_id和director_id,类似一个坐标,只要出现三次或者三次以上就打印出来我的解SELECTactor_id,director_idFROMActorDirectorGROUPBYactor_id,director_idHAVINGCOUNT(1)>=3我的解注解同时分组,两个出现次数大于等于3的就是符合的,看了下,其他的思路和这个......
  • 力扣:2028. 找出缺失的观测数据
    2028.找出缺失的观测数据现有一份 n+m 次投掷单个 六面 骰子的观测数据,骰子的每个面从 1 到 6 编号。观测数据中缺失了 n 份,你手上只拿到剩余 m 次投掷的数据。幸好你有之前计算过的这 n+m 次投掷数据的 平均值 。给你一个长度为 m 的整数数组 rolls......
  • 力扣刷题记录: 2134. 最少交换次数来组合所有的 1 Ⅱ
        这道题是第275场周赛的Q2,LC竞赛分为1748,主要考察滑动窗口。说实话这道题要想到是滑动窗口就很简单,否则就根本无从下手。方法一.滑动窗口(时间超过62.53%C++用户)        处理环形数组的一个很有效的技巧就是“追加”,把整个nums数组追加到nums数组后面,......
  • leetcode力扣 300. 最长递增子序列
    给你一个整数数组nums,找到其中最长严格递增子序列的长度。子序列是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7]是数组[0,3,1,6,2,2,7]的子序列。示例1:输入:nums=[10,9,2,5,3,7,101,18]输出:4解释:最长递增子序列是[2,3,7,101......
  • 力扣 32. 最长有效括号 python AC
    动态规划classSolution:deflongestValidParentheses(self,s):s=''+ssize=len(s)dp=[0]*sizeforiinrange(2,size):ifs[i]==')':ifs[i-1]=='(':......
  • leetcode力扣 1004. 最大连续1的个数 III
    给定一个二进制数组nums和一个整数k,如果可以翻转最多k个0,则返回数组中连续1的最大个数。示例1:输入:nums=[1,1,1,0,0,0,1,1,1,1,0],k=2输出:6解释:[1,1,1,0,0,1,1,1,1,1,1],翻转两个0后,最长的子数组长度为6。示例2:输入:nums=[0,0,1,1,0,0,1,1,1,0,1,1,0,0,0,1,1......
  • P1474 [USACO2.3] Money System / [USACO07OCT]Cow Cash G
    有点水,但是细究还是有点意思的题目https://www.luogu.com.cn/problem/P1474一开始的代码:#define_CRT_SECURE_NO_WARNINGS#include<iostream>#include<vector>#include<algorithm>#include<math.h>#include<sstream>#include<string>#include<......
  • 力扣数组题分享
    文章目录前言二分查找思路二分法第一种写法二分法第二种写法螺旋矩阵II思路结尾前言在学习数据结构的过程中,我通过力扣整理了一些常见的数据结构数组题。这些题目帮助我回顾了学习过程中的关键知识点。二分查找力扣题目704.二分查找给定一个n个元素有序......