每日一题:600.不含连续1的非负整数

题目:

给定一个正整数 n,找出小于或等于 n 的非负整数中,其二进制表示不包含 连续的1 的个数。

示例 1:

输入: 5
输出: 5
解释: 
下面是带有相应二进制表示的非负整数<= 5:
0 : 0
1 : 1
2 : 10
3 : 11
4 : 100
5 : 101
其中,只有整数3违反规则(有两个连续的1),其他5个满足规则。
说明: 1 <= n <= 10^9

思路:(动态规划)

设一个数组:用f(i)表示为长度为i不含连续1的二进制数(范围i个0~~i个1)的个数(类似于斐波那契数列

可以观察得出:

二进制长度
n=1n=2n=3n=4
00000000001000
10100100011001
1001000101010
1101100111011
10001001100
10101011101
11001101110
11101111111
2358

f(i) =f(i-1)+f(i-2)    当n=0时,设为1,即f(0)=1;

要查找到所有满足条件的数(即不重,不漏),可以就是将给定范围内的数分几个阶段,看各个阶段满足条件的数。

开始将一个数转换为二进制,从最高有效位(左->右)扫描,每当遇见一个1即(1*****,1后面有k个二进制位,每个*可以为1或0),满足不含有连续1的二进制数的总个数count 就需要增加f(k),因为我们可以在这个数字上放一个'0',后面放任意一个有效长度为k的字符串;之后,我们继续循环考虑剩下的情况,也就是说,我们在这个digit上放一个'1'。如果发现有连续的1,我们就退出循环并返回答案。在循环结束时,我们返回count+1,包括数字n本身。

例如,如果n是10010110,接下来需要找到比n小的且没有连续1的数:

1.我们只把第1个有效位1换为0,这个1后面怎么放数字,都会比n小,其中最大的数是末尾7位都是“1”的数,这些数可以用范围 00000000-01111111表示,其中满足不含有连续1的二进制数的个数为 f(7)。
2.我们只把第2个有效位1换为0,这个1后面怎么放数字,都会比n小,其中最大的数是末尾4位都是“1”的数,这些数可以用范围 10000000-10001111表示,数的个数为 0000-1111,其中满足不含有连续1的二进制数的个数为f(4)。
3.我们只把第3个有效位1换为0,这个1后面怎么放数字,都会比n小,其中最大的数是末尾2位都是“1”的数,这些数可以用范围10010000-10010011表示,数的个数为 00~11,其中满足不含有连续1的二进制数的个数为f(2)。
4.我们只把第4个有效位1换为0,这个1后面怎么放数字,都会比n小,其中最大的数是末尾1位是“1”的数,些数可以用范围 10010100-10010101表示,数的个数为 0~1,其中满足不含有连续1的二进制数的个数为f(1)。

注: 数的二进制表示中有几个1(比如,有k个1),这里就有几个(k个)步骤。

接下来的操作是需要在二进制数 1001011x 上做变换的,但1001011x已出现连续的1,非法了,故不进入计数。

图示:

 代码:

(这里是将二进制数字转换成了字符串,并且用了32长度的字符串存斐波那契数列的各个值,内存消耗过大)

class Solution {
public:
    

    //函数将一个数转换为二进制字符串
    string binaryStr(int num) {
        //定义一个字符串来接受转换而来的二进制字符串
        string st = "";
        while (num>0)
        {
            //将求出的数转换为对应字符'0'或'1'
            char c = num % 2 + '0';
            st.push_back(c);//这里是二进制低位在前高位在后
            num /= 2;
        }
        //反转,因为我们要的是高位在前低位在后
        reverse(st.begin(), st.end());
        return st;
    }
    //最后要返回
    int num=0;
    int findIntegers(int n) {
        //初始化斐波那契数列
        //int 32位
        vector<int> f(32);
        f[0] = 1;
        f[1] = 2;
        for (int i = 2; i < 32; i++) {
            f[i] = f[i - 1] + f[i - 2];
        }
        string str1 = binaryStr(n);
        for (int i = 0; i < str1.size(); i++) {
            
            if (str1[i] == '0') continue;
            num += f[str1.size() - 1 - i];
            if (i != 0 && str1[i - 1] == '1') return num;
        }
        //加1代表n本身也是一个
        return num + 1;
    }
};

第二版:写一个函数用来求斐波那契数列的各个值,用变量k表示有效数字1后面的二进制位数

class Solution {
public:
    //求斐波那契数列函数
    int feiB(int num) {
        int f_1 = 1, f_2 = 2, f_3 = 3;
        if (num == 0) return f_1;
        if (num == 1)return f_2;
        for (int i = 2; i <= num; i++){
            f_3 = f_1 + f_2;
            f_1 = f_2;
            f_2 = f_3;
         }
        return f_3;
    }
    int findIntegers(int n) {
        //最后要返回
        int num = 0;
        //记录前一位数字,因为前连续两位位1,后面不用看了
        int preBit = 0;
        //int型数据最大为2^31-1,31位最多有数字,取不到32位
        int k = 30;
        while (k>=0)
        {
            //1左移30位,即1后面30个0
            //判断k位是否为1
            if (n & (1 << k)) {
                //cout << k<<endl;;
                num += feiB(k);
                if (preBit) return num;
                preBit = 1;
            }
            else
            {
                preBit = 0;
            }
            k--;
        }
        return num + 1;
    }

};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值