【LeetCode每日一题】2024年8月第二周(上)

2024.8.5 困难

链接:600. 不含连续1的非负整数

(1)题目描述:

(2)示例

(3)分析

思路1:

题目要求的数值,是将数二进制转换后,不存在连续的1,那么我们初步分析:每一个数的最高位,由下一位来决定。第二位为0时:下一位可为0、1;而第二位为1时:下一位只为0

于是直接进行转换,按照n转换为2进制的位数后的长度,进行递归拼接即可。最后统计,只要转换后的数字,小于等于n就行。

这种方法,虽然可行,但在10^9数据量面前,显然不够看的,运行n=10^9时,耗时5s。

思路2:

因为实际上控制大小的是n转换为2进制后的长度 ,我们去观察,不同长度下,它的最大结果。(即当前长度下,所包含的个数)

① 我们惊人的发现,他们的结果满足类似斐波那契数列 的特点,因此我们完全可以写写出长度和个数的方程:dp[i] = dp[i - 1] + dp[i - 2];(dp用于存储个数,i为n二进制长度)

② 而进一步发现,我们可以得到:只要当前i位(从0开始)为1,那么结果中就一定包含:dp[i]个值:

怎么理解?

1)我们将dp结果看作是:i位1的数总和,比如符合题目可存在的最大数 101010(含自身)-- 6位 和1000000(不含自身) 皆对应d[6]=d[5]+d[4] ==》d4=d3+d2 d2=d1+d0 =d1+1

2) 而实际上d[6],同时满足:d[6]=d[5]+d[3]+d[2]=d[5]+d[3]+d[1]+1 ,很显然,这是和101010的1所在位数对应的,

3)那么对101001 6位:拆分为:100000 + 001000+ 000001 首先包含5位情况下的所有个数,再比对1000,再比对1==》100000:dp[5] 001000:dp[3] 000001:dp[0]

于是乎,我们得到:可以直接由当前n的二进制转换,得到最终的结果!

(4)代码

我把两种都写上:

java 复制代码
//思路2:
class Solution {
    // 动态规划的方法计算不含连续 1 的二进制数的数量
    public static int findIntegers(int n) {
        int[] dp = new int[32];//32位,最大n在此范围内
        dp[0] = 1;//初始化数据
        dp[1] = 2;
        // 预处理 dp 数组
        for (int i = 2; i < 32; i++) {
            dp[i] = dp[i - 1] + dp[i - 2]; //结果满足类似斐波那契数列的形式
        }

        int temp = 0;//检测最终值是否为2个1的情况
        int result = 0;
        //由上文遍历的思路,如此可以确定,只要当前i位为1,那么结果中就一定包含:dp[i]个前一位的东西
        // 怎么理解://将dp结果看作是:i位1的数总和,比如可存在的最大数 101010(含自身) 6位 和1000000(不含自身) 皆对应d[6]=d[5]+d[4] ==》d4=d3+d2 d2=d1+d0 =d1+1
        // 而实际上d[6],同时满足:d[6]=d[5]+d[3]+d[2]=d[5]+d[3]+d[1]+1 ,很显然,这是和101010的1所在位数对应的,
        // 那么对101001 6位:拆分为:100000 + 001000+ 000001 首先包含5位情况下的所有个数,再比对1000,再比对1==》
        // 100000:dp[5] 001000:dp[3] 000001:dp[0]

        // 遍历 n 的二进制表示
        for (int i = 31; i >= 0; i--) {
            if ((n & (1 << i)) != 0) {  // 当前位是 1按照上文输出
                // 实际上类如:11000 和10101相互等价皆为d[4]+d[3],所以不用else了......
                result += dp[i];
                if (temp == 1) {  // 检测到连续的两个 1
                    return result;
                }
                temp = 1;
            } else {
                temp = 0;
            }
        }
        return result + 1;  // 包含 n 本身
    }

    // 测试
    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        int sum = findIntegers(1000000000);
        System.out.println(sum);
        System.out.println("time: " + (System.currentTimeMillis() - start) + "ms");

    }
}
java 复制代码
//思路1:
class Solution1 {
    //遍历,直接超时!
    //思路:没有连续的1,那么每一个数的最高位:由下一位来决定。第二位为0时:可为0、1;1时:只为0.
    //统计时,只要大小小于就行。
    public static int findIntegers(int n) {
        int sum = 0;
        int len = Integer.toBinaryString(n).length();//Integer.toBinaryString:2进制转换
        List<String> str = Arrays.asList("0", "1");//赋初值
        if (n < 2) return n + 1;
        else {
            for (int i = 0; i < len; i++) {
                str = getS(str);
            }
            List<String> re = str.stream().sorted().filter(s -> n >= Integer.valueOf(s, 2)).collect(Collectors.toList()); 
            sum = re.size();
        }
        return sum;
    }

    public static List<String> getS(List<String> str) {
        List<String> stringList = new ArrayList<>();
        str.stream().forEach(s -> {
            if (s.charAt(0) == '0') {
                stringList.add("0" + s);
                stringList.add("1" + s);
            } else {
                stringList.add("0" + s);
            }
        });
        return stringList;
    }

    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        int sum = findIntegers(100000000);
        System.out.println(sum);
        System.out.println("time: " + (System.currentTimeMillis() - start) + "ms");
    }
}

(5)碎碎念

题目越短,事儿越大......一个题看了挺久的

2024.8.06 中等

链接:3129. 找出所有稳定的二进制数组 I

(1)题目描述:

(2)示例

(3)分析

题目中,limit限制着0或1的最大连续出现次数(最大为limit),我们按照顺序放0、1时,最后一位0/1是由:前一位没达到limit限制0、1添加0/1而来,转换为可以计算的,便是:不受限制(即所有)的0、1,减去不符合要求的(出现次数大于limit的。)

于是我们捋清楚了思路,而超过限制的明显由0或1组成,因此我们分类来讨论:

① 0结尾的:sum0 = 放0前以0/1结尾的总和 - 前面放了limit+1个0,以1结尾的

② 1结尾的:sum1 = 放1前以0/1结尾的总和 - 前面放了limit+1个1,以0结尾的

最后结果,sum1 + sum0

而此皆存在一个前提,当前放入的0/1总数,大于limit,如此才可能有重合。因此需要来表示当前存入的0/1数量==》

于是乎,大致思路有了,整体运用动态规划,设置i用于表示当前存入0的数量,j表示存入1的数量。再次观看案例,考虑到可能存在只放入同一个值(同时不超过limit和个数),其结果只有一种情况,由此可以设定初始值。

贴一张官方图:

(4)代码

java 复制代码
class Solution {
    public int numberOfStableArrays(int zero, int one, int limit) {
        final long MOD = 1000000007;
        long[][][] dp = new long[zero + 1][one + 1][2];
        for (int i = 0; i <= Math.min(zero, limit); i++) {
        // 初始化,设置最基础的可能性,只填充同一个值时,只有一种情况。赋值1;
            dp[i][0][0] = 1;
        }
        for (int i = 0; i <= Math.min(one, limit); i++) {
            dp[0][i][1] = 1;
        }
        for (int i = 1; i <= zero; i++) {
            for (int j = 1; j <= one; j++) {
                if (i > limit) {
                    dp[i][j][0] = dp[i - 1][j][0] + dp[i - 1][j][1] - dp[i - limit - 1][j][1];
                } else {
                    dp[i][j][0] = dp[i - 1][j][0] + dp[i - 1][j][1];
                }
                // dp[i][j][0]%= MOD;不这样写是因为,数据量大了后,会溢出,对应的数据变成负数了......
                dp[i][j][0] = (dp[i][j][0] % MOD + MOD) % MOD;// 数据回正
                if (j > limit) {
                    dp[i][j][1] = dp[i][j - 1][0] + dp[i][j - 1][1] - dp[i][j - limit - 1][0];
                } else {
                    dp[i][j][1] = dp[i][j - 1][0] + dp[i][j - 1][1];

                }
                dp[i][j][1] = (dp[i][j][1] % MOD + MOD) % MOD;

            }
        }
        return (int) ((dp[zero][one][0] + dp[zero][one][1]) % MOD);
    }
}

(5)碎碎念

你管这叫中等题?一开始我想的是用组合和乘法原理的思想,希望得到一个通解公式,类似那种伯努利装错信的思想,后来发现得不到有效的思路......还是太菜了。然后看了题解的提示,才有的思路。

2024.8.07 困难

链接:3130. 找出所有稳定的二进制数组 II

(1)题目描述:

(2)示例

(3)分析

这题目,实际上就是上面的,只不过数据量变大了,但是咱们的思路是完全正确的,而且取模后数据也不会超,直接试一试就行。Ctrl c v。

这里放一个灵神的解析,采用:**容斥原理+乘法原理 ,**我一开始的思想就类似这个,但没求出来,看到解析发现本来思路的可行性,灵神nb!

灵茶山艾府解析 - 力扣(LeetCode)

(4)代码

. - 力扣(LeetCode) 贴个代码。

java 复制代码
class Solution {
    private static final int MOD = 1_000_000_007;
    private static final int MX = 1001;

    private static final long[] F = new long[MX]; // f[i] = i!
    private static final long[] INV_F = new long[MX]; // inv_f[i] = i!^-1

    static {
        F[0] = 1;
        for (int i = 1; i < MX; i++) {
            F[i] = F[i - 1] * i % MOD;
        }

        INV_F[MX - 1] = pow(F[MX - 1], MOD - 2);
        for (int i = MX - 1; i > 0; i--) {
            INV_F[i - 1] = INV_F[i] * i % MOD;
        }
    }

    public int numberOfStableArrays(int zero, int one, int limit) {
        if (zero > one) {
            // swap,保证空间复杂度为 O(min(zero, one))
            int t = zero;
            zero = one;
            one = t;
        }
        long[] f0 = new long[zero + 3];
        for (int i = (zero - 1) / limit + 1; i <= zero; i++) {
            f0[i] = comb(zero - 1, i - 1);
            for (int j = 1; j <= (zero - i) / limit; j++) {
                f0[i] = (f0[i] + (1 - j % 2 * 2) * comb(i, j) * comb(zero - j * limit - 1, i - 1)) % MOD;
            }
        }

        long ans = 0;
        for (int i = (one - 1) / limit + 1; i <= Math.min(one, zero + 1); i++) {
            long f1 = comb(one - 1, i - 1);
            for (int j = 1; j <= (one - i) / limit; j++) {
                f1 = (f1 + (1 - j % 2 * 2) * comb(i, j) * comb(one - j * limit - 1, i - 1)) % MOD;
            }
            ans = (ans + (f0[i - 1] + f0[i] * 2 + f0[i + 1]) * f1) % MOD;
        }
        return (int) ((ans + MOD) % MOD); // 保证结果非负
    }

    private long comb(int n, int m) {
        return F[n] * INV_F[m] % MOD * INV_F[n - m] % MOD;
    }

    private static long pow(long x, int n) {
        long res = 1;
        for (; n > 0; n /= 2) {
            if (n % 2 > 0) {
                res = res * x % MOD;
            }
            x = x * x % MOD;
        }
        return res;
    }
}

//作者:灵茶山艾府
//链接:https://leetcode.cn/problems/find-all-possible-stable-binary-arrays-ii/solutions/2758868/dong-tai-gui-hua-cong-ji-yi-hua-sou-suo-37jdi/
//来源:力扣(LeetCode)
//著作权归作者所有。非商业转载。

(5)碎碎念

今天早上,在地铁上打开Leetcode app,发现题目和昨天一样,直接在手机上写了,正好检测昨天的记忆,写完正好地铁到站,案例也过了,虽然运行时间和内存排名排不到前面罢了。

相关推荐
什么想法都无12 分钟前
stream
java·java stream
m0_7482336413 分钟前
WebService简介
java
love静思冥想14 分钟前
Stream `Collectors.toList()` 和 `Stream.toList()` 的区别(Java)
java·stream
Ch.yang32 分钟前
【Spring】 Bean 注入 HttpServletRequest 能保证线程安全的原理
java·spring·代理模式
web1508509664134 分钟前
基于Mysql、JavaScript、PHP、ajax开发的MBTI性格测试网站(前端+后端)
java
Kenneth風车34 分钟前
【机器学习(九)】分类和回归任务-多层感知机(Multilayer Perceptron,MLP)算法-Sentosa_DSML社区版 (1)111
算法·机器学习·分类
昙鱼42 分钟前
springboot创建web项目
java·前端·spring boot·后端·spring·maven
eternal__day42 分钟前
数据结构(哈希表(中)纯概念版)
java·数据结构·算法·哈希算法·推荐算法
天之涯上上1 小时前
JAVA开发 在 Spring Boot 中集成 Swagger
java·开发语言·spring boot
2402_857583491 小时前
“协同过滤技术实战”:网上书城系统的设计与实现
java·开发语言·vue.js·科技·mfc