leetcode600 二进制不含连续1的非负整数
给定一个正整数 n,找出小于或等于 n 的非负整数中,其二进制表示不包含连续的1的个数。
输入: 5
输出: 5
解释:
下面是带有相应二进制表示的非负整数<= 5:
0 : 0
1 : 1
2 : 10
3 : 11
4 : 100
5 : 101
其中,只有整数3违反规则(有两个连续的1),其他5个满足规则。
说明: 1 <= n <= 10^9
可以看到n的大小最高到达
1
0
9
10^9
109,肯定没法去暴力求解。
写这个问题之前我们可以先思考一下另一个问题,给定一个长度l,二进制长度为n且不包含连续的1的数个数。
我们可以使用动态规划的方法,构建一个数组
f
[
i
]
[
j
]
f[i][j]
f[i][j]表示长度为
i
i
i,最高位为j时的合法数的数量。
计算
f
[
i
]
[
j
]
f[i][j]
f[i][j]时,
- 假设
j
j
j为0,那么它剩下
i
−
1
i-1
i−1位的最高位无论是0还是1都满足条件,所以有
f [ i ] [ 0 ] = f [ i − 1 ] [ 0 ] + f [ i − 1 ] [ 1 ] f[i][0]=f[i-1][0]+f[i-1][1] f[i][0]=f[i−1][0]+f[i−1][1] - 假设
j
j
j位1,那么他剩下
i
−
1
i-1
i−1位的最高位必须是
0
0
0,否则会有连续的
1
1
1了,有
f [ i ] [ 1 ] = f [ i − 1 ] [ 0 ] f[i][1]=f[i-1][0] f[i][1]=f[i−1][0]
由此我们得到长度为 i i i的满足条件的合法数的数量。
接下来在解决该问题,找出小于等于
n
n
n的合法数的数量。
首先我们得到
n
n
n的二进制表示,假设长度为
i
i
i,设置
c
u
r
cur
cur为他的当前最高位
- 假设它的当前最高位为1,那么长度为 i i i的最高位为0的合法数一定满足小于等于 n n n的条件,所以我们把这个数量即 f [ i ] [ 0 ] f[i][0] f[i][0]累加到最终结果 a n s ans ans中去。对于长度为 i i i最高位为1的合法数,我们不能确定它是不是小于 n n n,所以不能直接把 f [ i ] [ 1 ] f[i][1] f[i][1]加到结果中去。此时我们已经把所有最高位为0的可能的数都计算 a n s ans ans中去了,那么我们接下来考虑的就是默认最高位为1的数了。
- 假设它当前最高位是0,那么长度为i最高位是1的合法数肯定不能加进去了,而最高位是0的合法数也不一定都能够加进去,但是最高位一定只能是0了,和上面的最高位是1是一样的考虑,那么我们接下来考虑的就是默认最高位为0的数了。
- 无论是0是1,最后最高位都会默认位一个确定的数,既然他是个常数,我们直接忽略,将最高位 c u r cur cur置为 c u r − 1 cur-1 cur−1,再执行前两块内容。直到 c u r cur cur为0或者同时出现两个1(前面不是每块执行完都默认了最高位是0或1了嘛)。
我们以当前最高位为1为例,把
f
[
i
]
[
0
]
f[i][0]
f[i][0]全算上了,
f
[
i
]
[
1
]
f[i][1]
f[i][1]我们没法判断哪些该算进去,假设下一个最高位还是1,那么
f
[
i
−
1
]
[
0
]
f[i-1][0]
f[i−1][0]的数就全能算上了,而我们又知道
f
[
i
]
[
1
]
=
f
[
i
−
1
]
[
0
]
f[i][1]=f[i-1][0]
f[i][1]=f[i−1][0],那么也就是说所有情况都被算进去了(因为剩下最多就
f
[
i
]
[
1
]
f[i][1]
f[i][1]种),同时又到达了出现连续两个1的终止情况,刚刚好。如果下一个最高位是0,从上面我们知道满足条件的合法数在
f
[
i
−
1
]
[
0
]
f[i-1][0]
f[i−1][0]中,然后我们又要再去看下一个最高位,直到最高位是1(位置为
c
u
r
cur
cur),我们才可以确定的把
f
[
c
u
r
]
[
0
]
f[cur][0]
f[cur][0]加进去,以此类推吧。
还有一个注意点就是边界条件,因为循环条件的原因,当你因为
c
u
r
cur
cur变为0而退出循环,会少加一个
f
[
1
]
[
0
]
f[1][0]
f[1][0],所以此时
a
n
s
ans
ans要加1
class Solution {
public:
int findIntegers(int n) {
//int是32位,稍微取大一点40
int N = 40;
//f[i][j],长度为i,最高位是j时的合法数的数量,
int f[N][2];
//长度为1的时候,如果j是0,满足条件的就只有0,如果j是1,满足条件的就1
f[1][0] = 1, f[1][1] = 1;
//模拟一下长度为2的时候,
//如果j是0,满足条件的就有00,01,就是上面长度为1,j为1和0之和,
//如果j是1,最高位为1,他的前一位必须为0,那么就是长度为1,j为0的情况
for(int i = 2; i < N; i++){
f[i][0] = f[i - 1][1] + f[i - 1][0];
//f[i][1] = f[i][0] + f[i - 1][0];
f[i][1] = f[i - 1][0];
}
int len = 0;
while(n >> len){
len ++;
}
int ans = 0;
int cur, pre;
while(len){
len--;
cur = (n >> len) & 1;
cout << cur << endl;
//如果是1,那么f中长度为len+1最高位是0对应的合法数都可以加进去,而如果是0,因为不能确定是否小于n,还得看下面的迭代
if(cur == 1) ans += f[len + 1][0];
//如果连续出现两次1,就不满足题目条件了,上一次循环我们已经把最高位(也就是这一次的前一位)为0的情况全加进去了,现在考虑的时候默认前一位为1了
if(cur == 1 && pre == 1) break;
//如果是最后一位,如果是1,那么上面就加了2,如果是0,因为后面不迭代了,得把对应的f[1][0]加进去,就是1。
if(len == 0) ans ++;
pre = cur;
}
return ans;
}
};