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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/non-negative-integers-without-consecutive-ones
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

这个题额怎么说,做的时候开了一个O(n)的数组,也就是说会卡在26 / 527 ,最后执行输入为100000000
但是我觉得这个思路还是挺有意思的,因为没想到用二维的dp,所以用了一维的dp,这个题可以参考树状数组的那个思想, 利用lowbit函数,获取最低位,然后思路如下
这个算是向前+向后dp,向前规划的过程为:
(在这里应该解释一下lowbit函数,就是比如说 1100,这个二进制数,这个最低位在4上,那lowbit就返回4,减去4之后为1000,也就是用lowbit这个方法能渐渐的由低到高消去所有位上的1)
对于某个数字i,获取lowbit(i),设置新的i为i-lowbit(i),这个新值为消去了最低位1的新i,我想要获取连续的1,怎么办?怎么向前更新?如果能在新的i的最低位的1的右边加一个1就能获得一个具有连续的1的新数字,把这个新数字用记号标记上,注意,这个新数字为新的i+lowbit(i新)/2【这里面的lowbit(i)为最后一个1,得除以2降位,才能放在右边】
向后规划过程如下:
这个是后加的,有没有觉得向前规划的过程有问题?问题出在哪?在于它不能完全检测出所有的含有连续1的数字,比如说13,1101不可能由任何数字向前更新得到,咋办?这个时候,就体现出存放前面数字的意义,我虽然向前传播检测不出来,但是我可以通过逐渐消去低位的1,查看得到的暂时的值的标记,看他是不是连续的数字,如果是的话,证明这个先行检测的数字也是有连续的1的。
代码如下(含有lowbit函数和实现细节)

class Solution(object):
    def lowbit(self,x):
        return x&(-x)
    def findIntegers(self, n):
        """
        :type n: int
        :rtype: int
        """ 
        count=0
        dp=[-1 for i in range(n+1)]
        #dp[i]==-1,就是不包含连续1,如果为1就是包含连续1
        for i in range(0,n+1):
            if(dp[i]==1):
                count+=1
            else:
                u=i
                while(u>=i and i!=0):#这块不加i为0的条件的话,陷入死循环,这也是个特例
                    delta=self.lowbit(u)
                    u=u-delta
                    if(u!=0 and dp[u]==1):
                        dp[i]=1#这是与前面的联系,这块本来打算不加的,但是实践表明,这块必须要有状态转移,如果没有的话,比如13只靠下面的几行是检测不出来13的性质的,只有逐渐消1,才可以
                    new_delta=self.lowbit(u)
                    temp=u+(int)(new_delta/2)
                    if(new_delta!=0 and temp<=n):
                         dp[temp]=1
                if(dp[i]==1):
                    count+=1
        return n+1-count         
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值