[Leetcode] 600. Non-negative Integers without Consecutive Ones 解题报告

本文介绍了一种高效算法,用于计算小于等于给定正整数n的所有不含连续1的二进制数的数量。通过巧妙利用菲波那切数列特性,该算法实现了O(1)的时间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目

Given a positive integer n, find the number of non-negative integers less than or equal to n, whose binary representations do NOT contain consecutive ones.

Example 1:

Input: 5
Output: 5
Explanation: 
Here are the non-negative integers <= 5 with their corresponding binary representations:
0 : 0
1 : 1
2 : 10
3 : 11
4 : 100
5 : 101
Among them, only integer 3 disobeys the rule (two consecutive ones) and the other 5 satisfy the rule. 

Note: 1 <= n <= 109

思路

确实是一道比较难的题目,参考了网上的解法。该解法基于如下两个观察:

1)在由'0'和'1'构成的长度为k的字符串中,不含连续'1'的字符串的个数为菲波那切数列f(k)。我觉得这个可以严格证明,但是为了简便,这里仅举例说明一下。例如我们假设k = 5,那么字符串的的范围为00000 - 11111。我们可以将该范围分为两个部分:[00000, 01111) U [10000, 10111)。 而任何大于等于110000的数字都不符合条件,因为其必定含有两个连续的1。[00000, 01111)其实就对应了f(4),而[10000, 10111)其实就对应了f(3),所以f(5) = f(4) + f(3)。

2)如果对字符串从左往右扫描,每次遇到一个1的时候,如果它的左边是0并且右面还有k位,那么就可以给结果加上f(k),这是因为我们可以将该位置为0,并且在之后的k位上添加任意合法的长度为k的字符串,其大小都不会超过该字符串并且符合条件。但是如果其左边是1并且后面还有k位,那么添加f(k)之后就应该立即返回,这是因为任何之后添加f(i) (i < k)所对应的结果都不符合条件,原因是当前已经出现了两个连续的1了。如果程序在循环中没有提前返回,那么说明n本身也是符合条件的,所以最后返回ans + 1。

该算法的时间复杂度是O(1),空间复杂度也是O(1),这是因为对于int而言k <= 32,实在是好的不能再好的算法了。当然如果不限制是int,而是任意大小的正整数,那么其时间复杂度和空间复杂度都是O(log(num)),也是非常好的算法了。

代码

class Solution {
public:
    int findIntegers(int num) {
        int f[32];
        f[0] = 1, f[1] = 2;
        for (int i = 2; i < 32; ++i) {
            f[i] = f[i-1] + f[i-2];
        }
        int ans = 0, k = 30, pre_bit = 0;
        while (k >= 0) {
            if (num & (1 << k)) {   // find the first '1' from right to left
                ans += f[k];
                if (pre_bit) {
                    return ans;
                }
                pre_bit = 1;
            }
            else {
                pre_bit = 0;
            }
            --k;
        }
        return ans + 1;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值