题目描述
给定一个正整数 n,找出小于或等于 n 的非负整数中,其二进制表示不包含连续的 1 的个数。
样例
输入: 5
输出: 5
解释:
下面是带有相应二进制表示的非负整数<= 5:
0 : 0
1 : 1
2 : 10
3 : 11
4 : 100
5 : 101
其中,只有整数3违反规则(有两个连续的1),其他5个满足规则。
注意
- $1 \le n \le 10^9$
算法
(数位统计) $O(\log n)$
- 按照数位统计的一般思路,从二进制的高位向底位,每次保证作为答案的数字不能超过 n。
- 首先预处理出长度为 i 的二进制串中,以 0 作为最高位且符合条件的二进制串的个数。这里从低位开始往高位递推,递推方程为 $f(i) = f(i - 1) + f(i - 2)$,其中,$f(0) = f(1) = 1$。$f(0)$ 无具体含义。
- 从高到底遍历 n 的二进制位,设计布尔变量 last_one,表示 n 的二进制位中上一位是否为 1,初始为 false。
- 若第 i 位为 1,累计答案 $f(i + 1)$,然后考察 last_one,若 last_one 为 true,则此时统计工作已经结束,直接返回 ans;否则,设置 last_one = true。
- 若第 i 位为 0,设置 last_one = false。
- 最终返回 ans + 1,这里的加 1 加的是 n 本身作为合法的答案。
下面以例子 10101100
为例分析:
- 第 7 位是 1,则答案累计 f(8),这里累计的是
00000000 - 01111111
的答案个数;此时 last_one = false,然后设置 last_one = true。 - 第 6 位是 0,设置 last_one = false。
- 第 5 位是 1,则答案累计 f(6),这里累计的是
10000000 - 10011111
的答案个数;此时 last_one = false,然后设置 last_one = true - 第 4 位是 0,设置 last_one = false。
- 第 3 位是 1,则答案累计 f(4),这里累计的是
10100000 - 10100111
的答案个数。此时 last_one = false,然后设置 last_one = true。 - 第 2 位是 1,则答案累计 f(3),这里累计的是
10101000 - 10101011
的答案个数。此时 last_one = true,无需再进行之后的统计,因为第 2 位不能放置 1,所以返回答案为 f(3) + f(4) + f(6) + f(8) = 55。
时间复杂度
- 只涉及到对二进制位的操作,故时间复杂度为 $O(\log n)$。
C++ 代码
class Solution {
public:
int findIntegers(int num) {
vector<int> f(31);
f[0] = f[1] = 1;
for (int i = 2; i < 31; i++)
f[i] = f[i - 1] + f[i - 2];
bool last_one = false;
int ans = 0;
for (int i = 29; i >= 0; i--) {
if (num & (1 << i)) {
ans += f[i + 1];
if (last_one)
return ans;
last_one = true;
}
else
last_one = false;
}
return ans + 1;
}
};
相似的想法, 但是用递归实现的。