Given a positive integer n
, return the number of the integers in the range [0, n]
whose binary representations do not contain consecutive ones.
Example 1:
Input: n = 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.
Example 2:
Input: n = 1 Output: 2
Example 3:
Input: n = 2 Output: 3
Constraints:
1 <= n <= 109
This problem requires finding the count of non-negative integers up to a given integer n
whose binary representations don't contain consecutive 1s. The most efficient approach is using dynamic programming (specifically, digit DP).
Core Idea:
The solution leverages a recursive approach with memoization to avoid redundant calculations. The key is to consider the binary representation of numbers and build the solution digit by digit.
dfs(i, pre, limit)
Function:
The recursive function dfs(i, pre, limit)
takes three arguments:
i
: The current bit position (starting from the most significant bit, i.e., the leftmost bit).pre
: The value of the previous bit (0 or 1). This helps to track whether consecutive 1s are present.limit
: A boolean flag indicating whether the current bit is constrained by the input number n
. If limit
is true, it means the current bit cannot exceed the corresponding bit in n
.Base Case:
If i
is less than 0, it implies that all bits have been processed, and a valid number without consecutive 1s has been found. The function returns 1 (counting this valid number).
Recursive Step:
The function iterates through possible values for the current bit j
(0 or 1, or up to the value of the corresponding bit in n
if limit
is true).
pre
and j
are both 1, it means consecutive 1s are present, so this combination is skipped.dfs(i - 1, j, limit and j == up)
is made to process the next bit. The limit
flag is updated to ensure constraints are followed.Memoization:
The @cache
decorator (in Python) or the f
array (in Java, C++, Go, TypeScript) acts as a memoization table to store the results of subproblems, preventing redundant computations.
Time and Space Complexity:
n
, which is log₂(n). Memoization ensures that each subproblem is solved only once.Code Examples (with detailed comments):
The provided code demonstrates the solution in several languages (Python, Java, C++, Go, TypeScript). The logic remains consistent across all languages; only syntax differs. Refer to the comments in each code snippet for a clear understanding.
Example (Python):
class Solution:
def findIntegers(self, n: int) -> int:
@lru_cache(None) # Memoization decorator
def dfs(i: int, pre: int, limit: bool) -> int:
if i < 0: # Base case: all bits processed
return 1
up = (n >> i & 1) if limit else 1 # Determine the upper bound for the current bit
ans = 0
for j in range(up + 1):
if pre and j: # Skip if consecutive 1s
continue
ans += dfs(i - 1, j, limit and j == up) # Recursive call
return ans
return dfs(n.bit_length() - 1, 0, True) # Start recursion from the most significant bit
The other languages follow a very similar structure, using appropriate memoization techniques. The core recursive logic and base case remain the same.