1088. Confusing Number II
Problem Description
A confusing number is defined as a number that changes into a different VALID number when it is rotated by 180 degrees. Certain digits, when rotated, transform into other digits (0 -> 0, 1 -> 1, 6 -> 9, 8 -> 8, 9 -> 6), while others (2, 3, 4, 5, 7) become invalid. A number is confusing if, after this transformation, it is a valid number and different from the original. The objective is to count all the confusing numbers within a given range [1, n].
Flowchart Walkthrough
To deduce the appropriate algorithm approach for Leetcode 1088. Confusing Number II, let's navigate through the Flowchart by considering the characteristics and requirements of the problem. Here's a step-by-step walkthrough:
-
Is it a graph?
- Answer: No. The problem revolves around numbers, not graph structures involving nodes connected by edges.
Next Question from Flowchart: Need to solve for kth smallest/largest?
-
Need to solve for kth smallest/largest?
- Answer: No. The problem involves identifying all numbers that fulfill a condition, not determining order or magnitude.
Next Question from Flowchart: Involves Linked Lists?
-
Involves Linked Lists?
- Answer: No. The problem deals with numeric manipulations and mappings rather than data structure traversal or manipulation.
Next Question from Flowchart: Does the problem have small constraints?
-
Does the problem have small constraints?
- Answer: Yes. Although technically we could be looking at large numbers, the set of valid confusing numbers under a given limit is small enough to consider more exhaustive strategies.
Next Question from Flowchart: Brute force / Backtracking?
-
Brute force / Backtracking?
- Answer: Yes. Given that the problem isn't suitable for a straightforward computational algorithm and involves generating and checking each confusing number up to a limit, backtracking is a fitting approach. It allows exploring potential numbers recursively while adhering to the conditions for confusing numbers.
Conclusion: The flowchart dictates using a backtracking approach for generating all confusing numbers up to the given limit, thus fitting the problem's requirements. This involves recursively forming numbers and checking their validity against the problem's conditions.
Intuition
The solution approach is based on a depth-first search (DFS) algorithm. By building the numbers digit by digit, we can explore all numbers that could potentially be confusing numbers, skipping any numbers that contain invalid digits. As we generate a number, we simultaneously calculate its rotated version.
The main idea is to iterate through each digit position of the possible confusing number, selecting from the digits that have a valid rotation (0, 1, 6, 8, and 9) and appending them to the number being formed. For every digit added, the rotated counterpart is also computed. We keep track of whether the current path of numbers is following the leading number in the given range, n
, as this determines the upper bound of the number that can be used in the current digit's place.
After forming the entire number by adding digits up to the length of n
, a check is performed to see if it is a confusing number— that is, its rotated form must be a valid number and should not be equal to the original number. Only then is it counted as a confusing number.
To avoid making the check each time we add a digit, we add the check only when the full length of the number has been reached.
The code uses these helper functions to perform a systematic search for all possible confusing numbers in the given range, making sure the algorithm is both efficient and exhaustive.
Learn more about Math and Backtracking patterns.
Solution Approach
The solution uses recursive depth-first search (DFS) to build potential confusing numbers one digit at a time. Here's a breakdown of how the solution approach is implemented:
-
Mapping Valid Rotations: The variable
d
is an array that maps each digit to its corresponding rotation. If a digit results in an invalid number when rotated, it maps to-1
. This mapping array is used to quickly check if a digit is valid and what it becomes upon rotation. -
Recursive DFS: The function
dfs
is the core recursive function that performs the depth-first search. It takes three arguments:pos
: the current digit position we are trying to fill,limit
: a boolean flag that tells us whether we are restricted by the digit at positionpos
of the original numbern
, andx
: the number formed so far in the DFS exploration.
-
Base Case for DFS: When
pos
is equal to the length of the string representation ofn
, we've reached the end of a potential number. At this point, we check ifx
is a confusing number using thecheck
function, which returns1
ifx
is confusing, and0
otherwise. -
Building Numbers Digit by Digit: Within the
dfs
function, the loop variablei
iterates over possible digits (from 0 to 9). However, we only process those that do not map to-1
ind
, which ensures we skip invalid digits. -
Limiting the Search Space: If
limit
isTrue
, meaning we are still following the digit pattern ofn
, we only consider digits up to the digit at positionpos
inn
. Otherwise, we explore all valid digits (up to 9). -
Recursive Call: For each valid digit
i
, a recursive call is made todfs
for the next position (pos + 1
), withlimit
updated to indicate whether we are still bound byn
's digits (this isTrue
only iflimit
wasTrue
andi
is the same as the digit atpos
inn
). The numberx
is also updated by appending the digiti
. -
Counting Confusing Numbers: The
ans
variable accumulates the number of valid confusing numbers found by subsequentdfs
calls. -
Function
check
: This function is used to determine if the numberx
is a confusing number. It does so by rotating each digit ofx
and forming the transformed numbery
. Ifx
is equal toy
, then the number isn't confusing, and the function returnsFalse
. Ifx
is not equal toy
, the function returnsTrue
, and it's a confusing number. -
Entry Point: The entry point of the solution is the call to
dfs(0, True, 0)
. The initial call is made withpos
set to 0 (starting at the first digit),limit
set toTrue
(bound by the first digit ofn
), andx
set to 0 (starting with an empty number). -
Result: After the
dfs
calls have been made, the total number of confusing numbers found within the range [1, n] is returned.
This approach is efficient because it systematically generates all possible confusing numbers up to n
, checking each one specifically upon reaching the final digit position and avoiding any unnecessary checks or generation of numbers with invalid digits.
Ready to land your dream job?
Unlock your dream job with a 2-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's consider a range [1, 25] to illustrate the solution approach.
-
Creating the Valid Rotations Map: We have an array
d
that maps digits to their rotations, whered = [0, 1, -1, -1, -1, -1, 9, -1, 8, 6]
. Digits mapping to-1
are invalid for our purposes. -
Recursive DFS Exploration:
- We start with
pos = 0
,limit = True
, andx = 0
. - The DFS recurses to build numbers and explore all possible confusing numbers within the range.
- We start with
-
Building Numbers:
- At
pos = 0
, we try digits0, 1, 6, 8, 9
(since others are invalid). - Assume we first pick digit
1
. We updatex
to be1
.
- At
-
Depth-First Search Moves:
- We progress to
pos = 1
. Since the second digit in25
is5
, which limits our choice to digits less than or equal to5
, we only try0, 1
. - With
1
already chosen, we now pick0
, sox
becomes10
.
- We progress to
-
Limit Checking:
- Now,
limit
is no longerTrue
as the second digit0
is not the same as5
in25
.
- Now,
-
Checking for Confusing Number:
- When the number has been fully constructed (we reach
pos
equal to the length ofn
), we check if it's a confusing number. - We do this by rotating each digit and comparing the rotated number to the original.
- Since
10
becomes01
after rotation, which is not a valid number because of the leading zero, it is not counted.
- When the number has been fully constructed (we reach
-
Continuing the Search:
- We continue the search and try other combinations like
11
,16
(becomes91
, a confusing number),18
, and19
(becomes61
, a confusing number) at the current level. - Each time we find a confusing number, we increment our count.
- We continue the search and try other combinations like
-
Result:
- After exploring all possibilities, the DFS backtracks and tries new paths until all possible numbers are checked.
- In our range [1, 25], we would find that
16
and19
are the confusing numbers. Thus, the solution would return2
.
The complete DFS search tree for this range is not fully shown here due to brevity, but it would consider all valid combinations, incrementing the count each time a confusing number is detected. The recursion ensures we explore all possible paths using valid digits and adhering to the constraints imposed by n
.
Solution Implementation
1class Solution:
2 def confusingNumberII(self, N: int) -> int:
3 # Mapping of valid confusing number digits to their 180-degree rotation representation.
4 # Note that 2, 3, 4, 5, and 7 are not valid since they don't form a digit when rotated.
5 # -1 indicates invalid digits.
6 digit_mapping = [0, 1, -1, -1, -1, -1, 9, -1, 8, 6]
7
8 # Helper function to check if a number is a confusing number by comparing
9 # the original number with its rotated version.
10 def is_confusing(x: int) -> bool:
11 rotated, original = 0, x
12 while original:
13 original, remainder = divmod(original, 10)
14 rotated_digit = digit_mapping[remainder]
15 # If the digit is not valid when rotated, return False
16 if rotated_digit == -1:
17 return False
18 rotated = rotated * 10 + rotated_digit
19 # Confusing number if the original number is different from the rotated number
20 return x != rotated
21
22 # The main recursive function performs a depth-first search to generate all
23 # possible numbers within the limit and counts the confusing ones.
24 def dfs(position: int, is_limited: bool, current_number: int) -> int:
25 # If we have constructed a number with the same number of digits as N
26 if position == len(N_str):
27 # Check if this number is a confusing number
28 return int(is_confusing(current_number))
29
30 count = 0
31 # If we are limited by the most significant digit of N, up is that digit,
32 # otherwise it's 9 because we can use any digit from 0 to 9.
33 upper_bound = int(N_str[position]) if is_limited else 9
34
35 # Try all digits from 0 to upper_bound
36 for i in range(upper_bound + 1):
37 # Only proceed if the digit i is valid when rotated
38 if digit_mapping[i] != -1:
39 # Continue to the next position, update is_limited based on current upper bound
40 # and update current_number.
41 count += dfs(position + 1, is_limited and i == upper_bound, current_number * 10 + i)
42
43 return count
44
45 # Convert the input number N to a string to easily access each digit
46 N_str = str(N)
47 # Start the DFS from the first position, with the limit flag set, and starting number as 0
48 return dfs(0, True, 0)
49
1class Solution {
2 private final int[] digitMapping = {0, 1, -1, -1, -1, -1, 9, -1, 8, 6}; // Mapping of digits to their confusing number counterparts
3 private String targetNumberString; // The target number converted to string to facilitate index-based access
4
5 // This method calculates the number of confusing numbers less than or equal to n
6 public int confusingNumberII(int n) {
7 targetNumberString = String.valueOf(n); // Convert to string to get each digit by index
8 return depthFirstSearch(0, 1, 0); // Start the recursive depth-first search from position 0 with limit 1
9 }
10
11 // Helper method for the recursive depth-first search
12 private int depthFirstSearch(int position, int limitFlag, int currentNumber) {
13 // Base case: if the current position is at the end of the string
14 if (position >= targetNumberString.length()) {
15 // Check if the current number is a confusing number and return 1 if true, 0 otherwise
16 return isConfusing(currentNumber) ? 1 : 0;
17 }
18
19 // Determine the upper bound for this digit - if we are at the limit, we take the digit from the target
20 int upperBound = limitFlag == 1 ? targetNumberString.charAt(position) - '0' : 9;
21 int count = 0; // Initialize the count of confusing numbers
22
23 // Iterate through all digits up to upperBound
24 for (int digit = 0; digit <= upperBound; ++digit) {
25 // Check if the digit maps to a valid confusing number digit
26 if (digitMapping[digit] != -1) {
27 // Recursive call to explore further digits, updating the limitFlag and currentNumber accordingly
28 count += depthFirstSearch(position + 1, limitFlag == 1 && digit == upperBound ? 1 : 0, currentNumber * 10 + digit);
29 }
30 }
31
32 // Return the total count of confusing numbers found
33 return count;
34 }
35
36 // Helper method to check if a number is a confusing number
37 private boolean isConfusing(int number) {
38 int transform = 0; // This will hold the transformed confusing number
39 for (int temp = number; temp > 0; temp /= 10) {
40 int digit = temp % 10; // Get the last digit of temp
41 transform = transform * 10 + digitMapping[digit]; // Map the digit and add it to the transformed number
42 }
43 return number != transform; // Return true if the original and transformed numbers are different
44 }
45}
46
1class Solution {
2public:
3 // This function is used to convert a given integer into its confusing number equivalent.
4 // A confusing number is one that when rotated 180 degrees becomes a different number.
5 // It returns true if the number is confusing, false otherwise.
6 bool isConfusingNumber(int x) {
7 string rotatedDigits = "01689"; // Only these digits are valid after rotation.
8 int rotated = 0; // The rotated number.
9 int original = x; // The original number for comparison.
10 int mapping[10] = {0, 1, -1, -1, -1, -1, 9, -1, 8, 6}; // Mapping from original to rotated digits.
11
12 while (x > 0) {
13 int digit = x % 10; // Get the last digit.
14 if (mapping[digit] == -1) // If it's not a valid digit for rotation, return false.
15 return false;
16 rotated = rotated * 10 + mapping[digit]; // Append the rotated digit.
17 x /= 10; // Remove the last digit from x.
18 }
19 return original != rotated; // The number is confusing if the original and the rotated are different.
20 }
21
22 // This recursive function explores all the combinations of valid digits to form confusing numbers.
23 // It accepts the current position in the digits string, a limit flag to indicate if this digit should
24 // not exceed the corresponding digit in N, and the current formed number.
25 int dfs(int pos, bool limit, int x, const string& s) {
26 if (pos == s.size()) { // If we've considered all digits
27 return isConfusingNumber(x) ? 1 : 0;
28 }
29 int maxDigit = limit ? s[pos] - '0' : 9; // Determine the maximum digit we can use.
30 int count = 0; // Count of confusing numbers.
31 for (int digit = 0; digit <= maxDigit; ++digit) { // Explore all possible digits.
32 if (rotatedDigits.find(digit) != string::npos) { // Skip invalid digits.
33 // If the current digit equals the maxDigit, we set limit to true.
34 // Otherwise, we can freely choose any valid digit, so limit is false.
35 count += dfs(pos + 1, limit && digit == maxDigit, x * 10 + digit, s);
36 }
37 }
38 return count;
39 }
40
41 // This function returns the total count of confusing numbers less than or equal to N.
42 int confusingNumberII(int N) {
43 // Convert N to string for easier manipulation.
44 string numberString = to_string(N);
45
46 // Start the DFS from position 0, with the limit flag set (since at the
47 // first digit, we can't exceed the first digit of N), and initial number as 0.
48 return dfs(0, true, 0, numberString);
49 }
50};
51
1function confusingNumberII(n: number): number {
2 // Converts the number 'n' to its string representation.
3 const numberAsString = n.toString();
4
5 // Mapping of digits to their respective confusing number transformations.
6 // A confusing number transformation maps 0→0, 1→1, 6→9, 8→8, 9→6.
7 // Digit that cannot be in a confusing number are marked with -1.
8 const digitMap: number[] = [0, 1, -1, -1, -1, -1, 9, -1, 8, 6];
9
10 // Helper function to check whether a number is confusing,
11 // meaning its digits rotate to be a different number.
12 const isConfusing = (x: number) => {
13 let rotatedNumber = 0;
14 // Rotate each digit and form the rotated number.
15 for (let temp = x; temp > 0; temp = Math.floor(temp / 10)) {
16 const digit = temp % 10;
17 rotatedNumber = rotatedNumber * 10 + digitMap[digit];
18 }
19 // A number is confusing if it is different from its rotated form.
20 return x !== rotatedNumber;
21 };
22
23 // Recursive function to perform depth-first search on the digits.
24 const dfs = (position: number, isLimited: boolean, currentNumber: number): number => {
25 // Base case: all digits have been processed.
26 if (position >= numberAsString.length) {
27 // Check if the formed number is confusing.
28 return isConfusing(currentNumber) ? 1 : 0;
29 }
30 // Set the upper limit for the current digit based on limit.
31 const upperLimit = isLimited ? parseInt(numberAsString[position]) : 9;
32 let count = 0; // Store the count of confusing numbers found.
33 // Try all possible digits from 0 to the upper limit for current position.
34 for (let i = 0; i <= upperLimit; ++i) {
35 // Only continue if the digit is a valid confusing digit (not -1).
36 if (digitMap[i] !== -1) {
37 // Perform DFS on the next position with updated limits and number.
38 count += dfs(position + 1, isLimited && i === upperLimit, currentNumber * 10 + i);
39 }
40 }
41 // Return the total count of confusing numbers for this path.
42 return count;
43 };
44
45 // Call the depth-first search starting from the first digit.
46 return dfs(0, true, 0);
47}
48
Time and Space Complexity
The given code defines a function confusingNumberII
that computes the number of confusing numbers less than or equal to n
. A confusing number is defined as a number that does not equal its rotated 180 degrees representation.
The main algorithm involves a recursive depth-first search (DFS) function dfs
. This function has three main parameters:
pos
: The current digit position being consideredlimit
: A boolean that indicates if the current path is bounded by the maximum number ofn
x
: The current number being constructed
The time complexity of the DFS depends on the length of the number n
, as recursion is going digit by digit. At each level of recursion, the algorithm iterates through possible digits (up to 10, or up + 1
which is the digit limit at the current pos
if limit
is True).
The recursive tree's branching factor on average is less than 10 due to the filtering out of invalid digits (where d[i] == -1
). The depth of the tree is len(s)
where s
is the string representation of n
. Therefore, in terms of n
, the time complexity is roughly O(10^len(s))
, which can be seen as O(n)
because the number of confusing numbers is less than or equal to n
.
The space complexity is primarily determined by the depth of the recursion call stack, which goes as deep as len(s)
. Hence, the space complexity is O(len(s))
.
Additionally, the auxiliary space used is constant for the array d
and the c integers used in the algorithm (x
, y
, t
, v
, and temporary variables used for iteration and recursion).
To give a formal computational complexity:
- Time Complexity:
O(n)
wheren
is the input number - Space Complexity:
O(len(str(n)))
wherelen(str(n))
represents the number of digits inn
Learn more about how to find time and space complexity quickly using problem constraints.
Which data structure is used to implement recursion?
Recommended Readings
Math for Technical Interviews How much math do I need to know for technical interviews The short answer is about high school level math Computer science is often associated with math and some universities even place their computer science department under the math faculty However the reality is that you
Backtracking Template Prereq DFS with States problems dfs_with_states Combinatorial search problems Combinatorial search problems involve finding groupings and assignments of objects that satisfy certain conditions Finding all permutations combinations subsets and solving Sudoku are classic combinatorial problems The time complexity of combinatorial problems often grows rapidly with the size of
LeetCode Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way we