Leetcode 474-一和零

这篇博客介绍了如何使用动态规划和回溯法解决一个二进制字符串数组的问题,目标是在不超过m个0和n个1的限制下找到最大子集长度。动态规划解决方案通过构建三维数组实现,而回溯法虽然能够找到解但可能会导致超时。文章提供了详细的代码示例来解释这两种方法的实现细节。
摘要由CSDN通过智能技术生成

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

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

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

在这里插入图片描述

题解

1.动态规划法

题解可参考liweiwei1419
题解可参考代码随想录

  • 动态规划:背包问题
  • 若用递归,则递归中的取字符串和不取就对应于背包问题中的拿物品和不拿物品
  • 1.dp[i][j][k]:取了前i个字符串后最多取得j个0和k个1的最大的子集的个数 dp=new int[strs.length+1][m+1][n+1](第一个维度+1是为了避免分类讨论(假设第一个字符串为空字符串),后面两个维度+1是因为要取到m和n)
  • 2.转移方程:dp[i][j][k]=Math.max(dp[i][j][k],dp[i-1][j-strs[i].zeros][k-strs[j].ones]+1)
  • 3.遍历方向 从左至右 从上至下
  • 4.返回值 dp[strs.length][m][n]
class Solution {
    public int findMaxForm(String[] strs, int m, int n) {
        int[][][] dp=new int[strs.length+1][m+1][n+1];
        for(int i=1;i<=strs.length;i++){
            //j,和k,代表的是背包的容量,如果都从1开始,那么就默认了,当j=0,k=1和j=1,k=0时不能装东西,但是比如“0”或者“1”就能装进去.所以j和k要从0开始
            for(int j=0;j<=m;j++){
                for(int k=0;k<=n;k++){
                    //统计strs[i]中1的个数
                    int ones=0;
                    //注意有下标偏移 strs[i-1]对应于strs[i]
                    for(int p=0;p<strs[i-1].length();p++){
                        if(strs[i-1].charAt(p)=='1') ones++;
                    }
                    //strs[i]中0的个数
                    int zeros=strs[i-1].length()-ones;
                    dp[i][j][k]=dp[i-1][j][k];//不取strs[i]的情况
                    //写动态转移方程时一定要考虑边界条件
                    if(j>=zeros&&k>=ones){
                        dp[i][j][k]=Math.max(dp[i][j][k],dp[i-1][j-zeros][k-ones]+1);//取strs[i]的情况
                    }
                }
            }
        }
        return dp[strs.length][m][n];
    }
}

回溯法(错误)

尝试了回溯法,因为超时只有部分用例通过

//回溯
class Solution {
    ArrayList<ArrayList<String>> res = new ArrayList<>();
    ArrayList<String> path = new ArrayList<>();
    public int findMaxForm(String[] strs, int m, int n) {
        dfs(0,0,0,strs,m,n);
        int count=0;
        for(int i=0;i<res.size();i++){
            count=Math.max(count,res.get(i).size());
            //System.out.print(res.get(i));
        }
        return count;
    }

    //i:当前0的个数  j:当前1的个数
    public void dfs(int i, int j,int index,String[] strs, int m, int n){
        //终止条件
        //注意i,j是上一层0和1的数量,而index是本层的strs编号,所以要分开讨论
        if(index>strs.length||i>m||j>n) return;
        //加入所有可能的结果
        res.add(new ArrayList<>(path));
        if(index==strs.length) return;
        //当前层操作
        //统计strs[index]中1的数目
        int count=0;
        for(int p=0;p<strs[index].length();p++){
            if(strs[index].charAt(p)=='1') count++;
        }
        //将strs[index]加入path
        path.add(strs[index]);
        //加入当前strs[index]
        dfs(i+strs[index].length()-count,j+count,index+1,strs,m,n);
        path.remove(path.size()-1);
        //不加当前strs[index]
        dfs(i,j,index+1,strs,m,n);

    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值