3377. Digit Operations to Make Two Integers Equal
Problem Description
You have two integers n
and m
that have the same number of digits. Your goal is to transform n
into m
through a series of operations while maintaining certain constraints.
Allowed Operations:
- Pick any digit in
n
that is not 9 and increase it by 1 - Pick any digit in
n
that is not 0 and decrease it by 1
Key Constraint:
- At no point during the transformation (including the initial value and after each operation) can
n
be a prime number
Cost Calculation:
The cost of the entire transformation is the sum of all values that n
takes during the process. This includes:
- The initial value of
n
- Every intermediate value after each operation
- The final value
m
Objective:
Find the minimum cost to transform n
into m
. If it's impossible to do so without encountering a prime number, return -1.
Example Walkthrough: If we need to transform 12 to 15:
- 12 (starting value, not prime)
- 13 (increase second digit by 1, but 13 is prime - not allowed!)
- We'd need to find an alternative path that avoids prime numbers
The solution uses:
- Sieve of Eratosthenes to precompute all prime numbers up to 99999
- Dijkstra's algorithm (priority queue) to find the minimum cost path from
n
tom
- Each state in the graph represents a number, and edges represent valid digit modifications that don't result in prime numbers
- The priority queue ensures we explore paths with lower cumulative costs first
Intuition
The problem asks us to find the minimum cost path from n
to m
, where each step has a cost (the value of the number at that step). This immediately suggests a shortest path problem in a graph.
Think of each possible number as a node in a graph. Two nodes are connected if we can transform one into the other by changing a single digit (increase by 1 or decrease by 1). However, we can't visit any node that represents a prime number - these nodes are essentially "blocked" or removed from our graph.
Since we want to minimize the total sum of all numbers we visit (not just the number of steps), we need a weighted shortest path algorithm. Each edge has a weight equal to the destination node's value. This is why Dijkstra's algorithm is perfect here - it finds the path with minimum total weight.
The key insights are:
- Graph representation: Each valid (non-prime) number is a node, and valid single-digit changes create edges between nodes
- Edge weights: The weight of moving to a number is that number itself (since we add it to our cost)
- Precomputation: Since we'll be checking many numbers for primality, it's efficient to precompute all primes up to 99999 using the Sieve of Eratosthenes
- State exploration: From any current number, we try modifying each digit (up or down) to generate neighboring states, but only add them to our queue if they're not prime
The priority queue in Dijkstra's ensures we always process the path with the lowest cumulative cost first, guaranteeing we find the optimal solution when we reach m
. If we exhaust all possibilities without reaching m
, it means there's no valid path avoiding prime numbers, so we return -1.
Learn more about Graph, Math, Shortest Path and Heap (Priority Queue) patterns.
Solution Approach
The implementation consists of three main components: prime number precomputation, graph traversal using Dijkstra's algorithm, and neighbor generation through digit manipulation.
1. Prime Number Precomputation (run_sieve
)
We use the Sieve of Eratosthenes to identify all prime numbers up to 99999:
self.sieve = [True] * 100000
self.sieve[0], self.sieve[1] = False, False
for i in range(2, 100000):
if self.sieve[i]:
for j in range(2 * i, 100000, i):
self.sieve[j] = False
This creates a boolean array where sieve[i] = True
means i
is prime. We mark 0 and 1 as non-prime, then for each prime i
, mark all its multiples as non-prime.
2. Main Search Algorithm (solve
)
We implement Dijkstra's algorithm using a min-heap priority queue:
pq = []
heapq.heappush(pq, (n, n)) # (cumulative_cost, current_number)
visited = set()
The queue stores tuples of (cumulative_cost, current_number)
. The heap ensures we always process the path with minimum cost first.
3. Neighbor Generation
For each number, we generate neighbors by modifying each digit:
s = list(str(cur)) # Convert number to list of digit characters
for i in range(len(s)):
c = s[i] # Store original digit for restoration
# Try increasing digit by 1 (if not 9)
if s[i] < '9':
s[i] = chr(ord(s[i]) + 1)
next_ = int(''.join(s))
if not self.sieve[next_] and next_ not in visited:
heapq.heappush(pq, (sum_ + next_, next_))
s[i] = c # Restore original digit
# Try decreasing digit by 1 (if not 0, and won't create leading zero)
if s[i] > '0' and not (i == 0 and s[i] == '1'):
s[i] = chr(ord(s[i]) - 1)
next_ = int(''.join(s))
if not self.sieve[next_] and next_ not in visited:
heapq.heappush(pq, (sum_ + next_, next_))
s[i] = c # Restore original digit
Key checks during neighbor generation:
- Don't increase a digit if it's already 9
- Don't decrease a digit if it's 0
- Don't decrease the first digit if it's 1 (would create a number with fewer digits)
- Only add non-prime, unvisited numbers to the queue
4. Main Entry Point (minOperations
)
The function first checks if either n
or m
is prime - if so, the transformation is impossible:
if self.sieve[n] or self.sieve[m]: return -1 return self.solve(n, m)
The algorithm continues until either:
- We reach
m
(return the cumulative cost) - The priority queue is empty (no path exists, return -1)
The time complexity is O(V log V)
where V is the number of reachable non-prime numbers, and space complexity is O(V)
for the visited set and priority queue.
Ready to land your dream job?
Unlock your dream job with a 3-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's transform 10 to 14 and find the minimum cost path.
Step 1: Check Initial Constraints
- Is 10 prime? No (10 = 2 × 5)
- Is 14 prime? No (14 = 2 × 7)
- Both are non-prime, so transformation is possible
Step 2: Initialize Dijkstra's Algorithm
- Priority Queue:
[(10, 10)]
(cost, current_number) - Visited:
{}
- Initial cost includes starting value: 10
Step 3: Process States
Iteration 1: Pop (10, 10)
- Mark 10 as visited
- Generate neighbors by modifying digits:
- First digit (1): Can't decrease (would make 00), increase to 2 → 20 (not prime)
- Second digit (0): Can't decrease (already 0), increase to 1 → 11 (prime - skip!)
- Add to queue:
[(30, 20)]
(cost = 10 + 20 = 30)
Iteration 2: Pop (30, 20)
- Mark 20 as visited
- Generate neighbors:
- First digit (2): Decrease to 1 → 10 (already visited - skip!) Increase to 3 → 30 (not prime)
- Second digit (0): Increase to 1 → 21 (not prime)
- Add to queue:
[(60, 30), (51, 21)]
→ Priority queue reorders to[(51, 21), (60, 30)]
Iteration 3: Pop (51, 21)
- Mark 21 as visited
- Generate neighbors:
- First digit (2): Decrease to 1 → 11 (prime - skip!) Increase to 3 → 31 (prime - skip!)
- Second digit (1): Decrease to 0 → 20 (already visited - skip!) Increase to 2 → 22 (not prime)
- Add to queue:
[(60, 30), (73, 22)]
→ Reorders to[(60, 30), (73, 22)]
Iteration 4: Pop (60, 30)
- Mark 30 as visited
- Generate neighbors:
- First digit (3): Decrease to 2 → 20 (already visited - skip!) Increase to 4 → 40 (not prime)
- Second digit (0): Increase to 1 → 31 (prime - skip!)
- Add to queue:
[(73, 22), (100, 40)]
Iteration 5: Pop (73, 22)
- Mark 22 as visited
- Generate neighbors:
- First digit (2): Decrease to 1 → 12 (not prime) Increase to 3 → 32 (not prime)
- Second digit (2): Decrease to 1 → 21 (already visited - skip!) Increase to 3 → 23 (prime - skip!)
- Add to queue:
[(85, 12), (100, 40), (105, 32)]
Iteration 6: Pop (85, 12)
- Mark 12 as visited
- Generate neighbors:
- First digit (1): Increase to 2 → 22 (already visited - skip!)
- Second digit (2): Decrease to 1 → 11 (prime - skip!) Increase to 3 → 13 (prime - skip!) Increase to 4 → 14 (TARGET FOUND!)
- Cost to reach 14 = 85 + 14 = 99
Final Answer: The minimum cost is 99
The path taken was: 10 → 20 → 21 → 22 → 12 → 14
- Cost = 10 + 20 + 21 + 22 + 12 + 14 = 99
This example demonstrates how:
- We avoid prime numbers (11, 13, 23, 31)
- We don't revisit already processed numbers
- The priority queue ensures we explore lower-cost paths first
- We accumulate the cost by adding each number we visit
Solution Implementation
1import heapq
2from typing import Set
3
4
5class Solution:
6 def __init__(self):
7 # Array to store prime/non-prime status for numbers up to 99999
8 self.sieve = []
9
10 def run_sieve(self) -> None:
11 """
12 Generate prime numbers using Sieve of Eratosthenes algorithm.
13 True indicates prime, False indicates non-prime.
14 """
15 # Initialize all numbers as prime initially
16 self.sieve = [True] * 100000
17
18 # 0 and 1 are not prime numbers
19 self.sieve[0], self.sieve[1] = False, False
20
21 # Mark multiples of each prime as non-prime
22 for i in range(2, 100000):
23 if self.sieve[i]: # If i is prime
24 # Mark all multiples of i as non-prime
25 for j in range(2 * i, 100000, i):
26 self.sieve[j] = False
27
28 def solve(self, n: int, m: int) -> int:
29 """
30 Find minimum sum path from n to m by changing one digit at a time.
31 Only non-prime numbers are valid intermediate states.
32
33 Args:
34 n: Starting number
35 m: Target number
36
37 Returns:
38 Minimum sum of all numbers in the path, or -1 if impossible
39 """
40 # Priority queue: (cumulative_sum, current_number)
41 priority_queue = []
42 heapq.heappush(priority_queue, (n, n))
43
44 # Set to track visited numbers to avoid cycles
45 visited: Set[int] = set()
46
47 while priority_queue:
48 cumulative_sum, current_number = heapq.heappop(priority_queue)
49
50 # Skip if already visited
51 if current_number in visited:
52 continue
53 visited.add(current_number)
54
55 # Found target
56 if current_number == m:
57 return cumulative_sum
58
59 # Convert number to list of digit characters for manipulation
60 digits = list(str(current_number))
61
62 # Try changing each digit
63 for i in range(len(digits)):
64 original_digit = digits[i]
65
66 # Try incrementing the digit (if not already 9)
67 if digits[i] < '9':
68 digits[i] = chr(ord(digits[i]) + 1)
69 next_number = int(''.join(digits))
70
71 # Add to queue if it's not prime and not visited
72 if not self.sieve[next_number] and next_number not in visited:
73 heapq.heappush(priority_queue,
74 (cumulative_sum + next_number, next_number))
75
76 # Restore original digit
77 digits[i] = original_digit
78
79 # Try decrementing the digit
80 # Cannot decrement if it's 0, or if it's the first digit and equals 1
81 # (to avoid creating numbers with leading zeros)
82 if digits[i] > '0' and not (i == 0 and digits[i] == '1'):
83 digits[i] = chr(ord(digits[i]) - 1)
84 next_number = int(''.join(digits))
85
86 # Add to queue if it's not prime and not visited
87 if not self.sieve[next_number] and next_number not in visited:
88 heapq.heappush(priority_queue,
89 (cumulative_sum + next_number, next_number))
90
91 # Restore original digit
92 digits[i] = original_digit
93
94 # No path found
95 return -1
96
97 def minOperations(self, n: int, m: int) -> int:
98 """
99 Main entry point to find minimum sum path from n to m.
100
101 Args:
102 n: Starting number
103 m: Target number
104
105 Returns:
106 Minimum sum of path, or -1 if either number is prime or no path exists
107 """
108 # Generate prime sieve
109 self.run_sieve()
110
111 # Check if either starting or target number is prime
112 if self.sieve[n] or self.sieve[m]:
113 return -1
114
115 # Find minimum sum path
116 return self.solve(n, m)
117
1class Solution {
2 private boolean[] isPrime;
3
4 /**
5 * Generates a sieve of Eratosthenes to identify prime numbers up to 99999
6 */
7 private void generatePrimeSieve() {
8 isPrime = new boolean[100000];
9 Arrays.fill(isPrime, true);
10 isPrime[0] = false;
11 isPrime[1] = false;
12
13 // Sieve of Eratosthenes algorithm
14 for (int i = 2; i < 100000; i++) {
15 if (isPrime[i]) {
16 // Mark all multiples of i as non-prime
17 for (int j = 2 * i; j < 100000; j += i) {
18 isPrime[j] = false;
19 }
20 }
21 }
22 }
23
24 /**
25 * Finds the minimum sum path from source to target using Dijkstra's algorithm
26 * @param source Starting number
27 * @param target Destination number
28 * @return Minimum sum of all numbers in the path, or -1 if no path exists
29 */
30 private int findMinimumPathSum(int source, int target) {
31 // Priority queue storing [pathSum, currentNumber], ordered by pathSum
32 PriorityQueue<int[]> priorityQueue = new PriorityQueue<>(
33 Comparator.comparingInt(entry -> entry[0])
34 );
35 priorityQueue.add(new int[] {source, source});
36
37 Set<Integer> visitedNumbers = new HashSet<>();
38
39 while (!priorityQueue.isEmpty()) {
40 int[] current = priorityQueue.poll();
41 int pathSum = current[0];
42 int currentNumber = current[1];
43
44 // Skip if already visited
45 if (visitedNumbers.contains(currentNumber)) {
46 continue;
47 }
48 visitedNumbers.add(currentNumber);
49
50 // Check if target reached
51 if (currentNumber == target) {
52 return pathSum;
53 }
54
55 // Convert current number to character array for digit manipulation
56 char[] digits = String.valueOf(currentNumber).toCharArray();
57
58 // Try changing each digit
59 for (int digitIndex = 0; digitIndex < digits.length; digitIndex++) {
60 char originalDigit = digits[digitIndex];
61
62 // Try incrementing the digit (if not already 9)
63 if (digits[digitIndex] < '9') {
64 digits[digitIndex] = (char) (digits[digitIndex] + 1);
65 int nextNumber = Integer.parseInt(new String(digits));
66
67 // Add to queue if it's not prime and not visited
68 if (!isPrime[nextNumber] && !visitedNumbers.contains(nextNumber)) {
69 priorityQueue.add(new int[] {pathSum + nextNumber, nextNumber});
70 }
71 digits[digitIndex] = originalDigit; // Restore original digit
72 }
73
74 // Try decrementing the digit (if valid)
75 // Avoid creating numbers with leading zeros
76 if (digits[digitIndex] > '0' &&
77 !(digitIndex == 0 && digits[digitIndex] == '1')) {
78
79 digits[digitIndex] = (char) (digits[digitIndex] - 1);
80 int nextNumber = Integer.parseInt(new String(digits));
81
82 // Add to queue if it's not prime and not visited
83 if (!isPrime[nextNumber] && !visitedNumbers.contains(nextNumber)) {
84 priorityQueue.add(new int[] {pathSum + nextNumber, nextNumber});
85 }
86 digits[digitIndex] = originalDigit; // Restore original digit
87 }
88 }
89 }
90
91 // No path found
92 return -1;
93 }
94
95 /**
96 * Main method to find minimum operations from n to m
97 * @param n Starting number
98 * @param m Target number
99 * @return Minimum sum of path from n to m, or -1 if impossible
100 */
101 public int minOperations(int n, int m) {
102 generatePrimeSieve();
103
104 // Cannot start or end at a prime number
105 if (isPrime[n] || isPrime[m]) {
106 return -1;
107 }
108
109 return findMinimumPathSum(n, m);
110 }
111}
112
1class Solution {
2private:
3 vector<bool> isPrime;
4
5 /**
6 * Generate prime sieve using Sieve of Eratosthenes algorithm
7 * Marks all prime numbers up to 100,000
8 */
9 void generatePrimeSieve() {
10 isPrime.resize(100000, true);
11 isPrime[0] = false;
12 isPrime[1] = false;
13
14 for (int i = 2; i < 100000; ++i) {
15 if (isPrime[i]) {
16 // Mark all multiples of i as non-prime
17 for (int j = 2 * i; j < 100000; j += i) {
18 isPrime[j] = false;
19 }
20 }
21 }
22 }
23
24 /**
25 * Find minimum sum path from n to m by modifying digits
26 * Uses Dijkstra's algorithm with priority queue
27 * @param startNum: starting number
28 * @param targetNum: target number to reach
29 * @return minimum sum of all numbers in the path, or -1 if impossible
30 */
31 int findMinimumPath(int startNum, int targetNum) {
32 // Min heap: {sum, current_number}
33 priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> minHeap;
34 unordered_set<int> visited;
35
36 // Initialize with starting number
37 minHeap.push({startNum, startNum});
38
39 while (!minHeap.empty()) {
40 int currentSum = minHeap.top().first;
41 int currentNum = minHeap.top().second;
42 minHeap.pop();
43
44 // Skip if already visited
45 if (visited.find(currentNum) != visited.end()) {
46 continue;
47 }
48 visited.insert(currentNum);
49
50 // Check if we reached the target
51 if (currentNum == targetNum) {
52 return currentSum;
53 }
54
55 // Convert current number to string for digit manipulation
56 string numStr = to_string(currentNum);
57
58 // Try modifying each digit
59 for (int digitIndex = 0; digitIndex < numStr.size(); ++digitIndex) {
60 char originalDigit = numStr[digitIndex];
61
62 // Try incrementing the digit (if not already 9)
63 if (numStr[digitIndex] < '9') {
64 numStr[digitIndex]++;
65 int nextNum = stoi(numStr);
66
67 // Add to queue if it's not prime and not visited
68 if (!isPrime[nextNum] && visited.find(nextNum) == visited.end()) {
69 minHeap.push({currentSum + nextNum, nextNum});
70 }
71 numStr[digitIndex] = originalDigit; // Restore original digit
72 }
73
74 // Try decrementing the digit (avoid leading zeros)
75 if (numStr[digitIndex] > '0' && !(digitIndex == 0 && numStr[digitIndex] == '1')) {
76 numStr[digitIndex]--;
77 int nextNum = stoi(numStr);
78
79 // Add to queue if it's not prime and not visited
80 if (!isPrime[nextNum] && visited.find(nextNum) == visited.end()) {
81 minHeap.push({currentSum + nextNum, nextNum});
82 }
83 numStr[digitIndex] = originalDigit; // Restore original digit
84 }
85 }
86 }
87
88 // No path found
89 return -1;
90 }
91
92public:
93 /**
94 * Main function to find minimum operations from n to m
95 * @param n: starting number
96 * @param m: target number
97 * @return minimum sum of path or -1 if impossible
98 */
99 int minOperations(int n, int m) {
100 // Generate prime sieve
101 generatePrimeSieve();
102
103 // Check if either n or m is prime (impossible to reach/start from prime)
104 if (isPrime[n] || isPrime[m]) {
105 return -1;
106 }
107
108 // Find minimum path sum
109 return findMinimumPath(n, m);
110 }
111};
112
1let isPrime: boolean[] = [];
2
3/**
4 * Generate prime sieve using Sieve of Eratosthenes algorithm
5 * Marks all prime numbers up to 100,000
6 */
7function generatePrimeSieve(): void {
8 // Initialize array with all values set to true
9 isPrime = new Array(100000).fill(true);
10 isPrime[0] = false;
11 isPrime[1] = false;
12
13 for (let i = 2; i < 100000; i++) {
14 if (isPrime[i]) {
15 // Mark all multiples of i as non-prime
16 for (let j = 2 * i; j < 100000; j += i) {
17 isPrime[j] = false;
18 }
19 }
20 }
21}
22
23/**
24 * Find minimum sum path from n to m by modifying digits
25 * Uses Dijkstra's algorithm with priority queue
26 * @param startNum - starting number
27 * @param targetNum - target number to reach
28 * @returns minimum sum of all numbers in the path, or -1 if impossible
29 */
30function findMinimumPath(startNum: number, targetNum: number): number {
31 // Min heap: [sum, currentNumber]
32 // Using array to simulate priority queue, will sort manually
33 const minHeap: [number, number][] = [];
34 const visited = new Set<number>();
35
36 // Initialize with starting number
37 minHeap.push([startNum, startNum]);
38
39 while (minHeap.length > 0) {
40 // Sort to simulate min heap behavior (by sum)
41 minHeap.sort((a, b) => a[0] - b[0]);
42
43 const [currentSum, currentNum] = minHeap.shift()!;
44
45 // Skip if already visited
46 if (visited.has(currentNum)) {
47 continue;
48 }
49 visited.add(currentNum);
50
51 // Check if we reached the target
52 if (currentNum === targetNum) {
53 return currentSum;
54 }
55
56 // Convert current number to string for digit manipulation
57 const numStr = currentNum.toString();
58 const digits = numStr.split('');
59
60 // Try modifying each digit
61 for (let digitIndex = 0; digitIndex < digits.length; digitIndex++) {
62 const originalDigit = digits[digitIndex];
63
64 // Try incrementing the digit (if not already 9)
65 if (digits[digitIndex] < '9') {
66 digits[digitIndex] = String(Number(digits[digitIndex]) + 1);
67 const nextNum = parseInt(digits.join(''));
68
69 // Add to queue if it's not prime and not visited
70 if (!isPrime[nextNum] && !visited.has(nextNum)) {
71 minHeap.push([currentSum + nextNum, nextNum]);
72 }
73 digits[digitIndex] = originalDigit; // Restore original digit
74 }
75
76 // Try decrementing the digit (avoid leading zeros)
77 if (digits[digitIndex] > '0' && !(digitIndex === 0 && digits[digitIndex] === '1')) {
78 digits[digitIndex] = String(Number(digits[digitIndex]) - 1);
79 const nextNum = parseInt(digits.join(''));
80
81 // Add to queue if it's not prime and not visited
82 if (!isPrime[nextNum] && !visited.has(nextNum)) {
83 minHeap.push([currentSum + nextNum, nextNum]);
84 }
85 digits[digitIndex] = originalDigit; // Restore original digit
86 }
87 }
88 }
89
90 // No path found
91 return -1;
92}
93
94/**
95 * Main function to find minimum operations from n to m
96 * @param n - starting number
97 * @param m - target number
98 * @returns minimum sum of path or -1 if impossible
99 */
100function minOperations(n: number, m: number): number {
101 // Generate prime sieve
102 generatePrimeSieve();
103
104 // Check if either n or m is prime (impossible to reach/start from prime)
105 if (isPrime[n] || isPrime[m]) {
106 return -1;
107 }
108
109 // Find minimum path sum
110 return findMinimumPath(n, m);
111}
112
Time and Space Complexity
Time Complexity: O(V * D * log(V))
where V
is the number of valid non-prime numbers in the range and D
is the number of digits.
- The sieve of Eratosthenes takes
O(N * log(log(N)))
whereN = 100000
- In the worst case, we visit all valid non-prime numbers between 1 and 100000
- For each number visited, we perform:
- String conversion:
O(D)
whereD
is the number of digits (max 5 for numbers < 100000) - For each digit position, we try at most 2 operations (increment and decrement):
O(D)
- Each heap push operation:
O(log(V))
whereV
is the size of the heap
- String conversion:
- The heap can contain at most all valid non-prime numbers
- Overall:
O(V * D * log(V))
for the main algorithm plusO(N * log(log(N)))
for the sieve
Space Complexity: O(N + V)
- Sieve array:
O(100000)
=O(N)
- Heap: Can contain at most
O(V)
elements whereV
is the number of valid non-prime numbers - Visited set: Can contain at most
O(V)
elements - String manipulation uses
O(D)
temporary space whereD
is the number of digits (max 5) - Overall:
O(N + V)
whereN = 100000
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Incorrect Handling of Leading Zeros
One of the most common mistakes is allowing the first digit to be decremented to 0, which would create an invalid number or a number with fewer digits.
Pitfall Example:
# WRONG: This allows 1234 to become 0234, which is interpreted as 234
if digits[i] > '0':
digits[i] = chr(ord(digits[i]) - 1)
next_number = int(''.join(digits)) # 1234 -> "0234" -> 234 (wrong!)
Solution: Always check if we're modifying the first digit and it's currently '1':
# CORRECT: Prevent first digit from becoming 0
if digits[i] > '0' and not (i == 0 and digits[i] == '1'):
digits[i] = chr(ord(digits[i]) - 1)
next_number = int(''.join(digits))
2. Not Restoring the Original Digit After Each Modification
When trying different digit modifications, forgetting to restore the original digit leads to cumulative changes that produce incorrect neighbors.
Pitfall Example:
# WRONG: Digit not restored, affecting subsequent iterations
for i in range(len(digits)):
if digits[i] < '9':
digits[i] = chr(ord(digits[i]) + 1)
# Process next_number...
# Missing: digits[i] = original_digit
if digits[i] > '0': # This now operates on the modified digit!
digits[i] = chr(ord(digits[i]) - 1)
Solution: Store and restore the original digit after each operation:
# CORRECT: Save and restore original digit
for i in range(len(digits)):
original_digit = digits[i] # Save original
if digits[i] < '9':
digits[i] = chr(ord(digits[i]) + 1)
# Process next_number...
digits[i] = original_digit # Restore
if digits[i] > '0' and not (i == 0 and digits[i] == '1'):
digits[i] = chr(ord(digits[i]) - 1)
# Process next_number...
digits[i] = original_digit # Restore
3. Processing Already Visited Nodes Multiple Times
Without proper visited state checking, the same number might be processed multiple times with different cumulative costs, leading to incorrect results or infinite loops.
Pitfall Example:
# WRONG: Adding to visited set after processing neighbors while priority_queue: cumulative_sum, current_number = heapq.heappop(priority_queue) if current_number == m: return cumulative_sum # Generate and add neighbors... visited.add(current_number) # Too late! Might process same number again
Solution: Check and mark visited status immediately after popping from the queue:
# CORRECT: Check visited status right after popping while priority_queue: cumulative_sum, current_number = heapq.heappop(priority_queue) if current_number in visited: continue # Skip if already processed visited.add(current_number) # Mark as visited immediately if current_number == m: return cumulative_sum
4. Incorrect Priority Queue Tuple Order
Using the wrong order in the priority queue tuple causes the algorithm to not prioritize minimum cost paths.
Pitfall Example:
# WRONG: Current number as first element makes heap sort by number value heapq.heappush(priority_queue, (current_number, cumulative_sum))
Solution: Always put the cumulative sum first for proper minimum cost prioritization:
# CORRECT: Cumulative sum first for minimum cost priority heapq.heappush(priority_queue, (cumulative_sum, current_number))
5. Off-by-One Errors in Sieve Implementation
Incorrectly implementing the sieve bounds or loop ranges can mark wrong numbers as prime/non-prime.
Pitfall Example:
# WRONG: Starting from i instead of 2*i marks prime itself as non-prime
for i in range(2, 100000):
if self.sieve[i]:
for j in range(i, 100000, i): # Should start from 2*i
self.sieve[j] = False
Solution: Start marking multiples from 2*i, not i itself:
# CORRECT: Start from 2*i to preserve the prime number itself
for i in range(2, 100000):
if self.sieve[i]:
for j in range(2 * i, 100000, i):
self.sieve[j] = False
What's the relationship between a tree and a graph?
Recommended Readings
https assets algo monster cover_photos graph svg Graph Fundamentals Tree with 0 cycle At this point you should be pretty familiar with trees A tree is a special kind of graph a connected acyclic cycle less graph A graph may contain cycle s and nodes could be disconnected A tree
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
Shortest Path Between A and B Prereq BFS on Graph problems graph_bfs Given an unweighted connected graph return the length of the shortest path between two nodes A and B in terms of the number of edges Assume there always exists a path between nodes A and B Input graph
Want a Structured Path to Master System Design Too? Don’t Miss This!