题目:
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;
}
};