每日一题: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=1 n=2 n=3 n=4
0 00 000 0000 1000
1 01 001 0001 1001
10 010 0010 1010
11 011 0011 1011
100 0100 1100
101 0101 1101
110 0110 1110
111 0111 1111
2 3 5 8

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,非法了,故不进入计数。

图示:

每日一题:600.不含连续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;
    }

};

上一篇:CCF-CSP考试2021年4月第2题(202104-2邻域均值)


下一篇:数塔取数问题