[LeetCode] 600. 不含连续1的非负整数(Python)

297 阅读1分钟

👉 “Offer 驾到,掘友接招!我正在参与2022春招打卡活动点击查看 活动详情

600. 不含连续1的非负整数

题目描述

给定一个正整数 n ,返回范围在 [0, n] 都非负整数中,其二进制表示不包含 连续的 1 的个数。

示例 1:

输入: n = 5
输出: 5
解释: 
下面是带有相应二进制表示的非负整数<= 5
0 : 0
1 : 1
2 : 10
3 : 11
4 : 100
5 : 101
其中,只有整数3违反规则(有两个连续的1),其他5个满足规则。

示例 2:

输入: n = 1
输出: 2

示例 3:

输入: n = 2
输出: 3

提示:

  • 1 <= n <= 109

解题思路:

对于2n2^n,例如100000,不符合条件的有三类,数量表示为f(n)

  • 110000 - 11xxxx,共 2n22^{n-2}
  • 小于010000的,即f(n1)f(n-1)
  • 大于100000小于101000的,即f(n2)f(n-2) 综上所述f(n)=2n2+f(n1)+f(n2),f(0)=f(1)=0f(n)=2^{n-2}+f(n-1)+f(n-2), f(0) = f(1) = 0

后面的循环实际上相当于每次判断最高两位,跳过前置0

计算出不符合条件的数的数量 MM 后,即可得符合条件得数得数量 nM+1n-M+1,因为包含0

代码如下:

class Solution:
    def findIntegers(self, n: int) -> int:
        if n < 6:
            return n - (n+1) // 4 + 1
        dp = {1: 0, 2: 0}
        nn = 2
        while nn < n:
            nn <<= 1
            dp[nn] = dp[nn >> 1] + dp[nn >> 2] + (nn >> 2)
        if n == nn:
            return nn - dp[nn] + 1

        ans = n + 1
        while True:
            while nn > n:
                nn >>= 1
            if n == nn:
                ans -= dp[nn]
                break
            if n >= nn * 3 // 2:
                nn <<= 1
                ans -= dp[nn] - nn + n + 1
                break
            else:
                ans -= dp[nn]
                n -= nn
        return ans

  • 时间复杂度 O(logn)O(logn)
  • 空间复杂度 O(logn)O(logn)