1922. Count Good Numbers
Problem Description
You need to count how many "good" digit strings can be formed with a given length n
.
A digit string is considered good when:
- Digits at even indices (0, 2, 4, ...) must be even numbers (0, 2, 4, 6, 8)
- Digits at odd indices (1, 3, 5, ...) must be prime numbers (2, 3, 5, 7)
Note that indexing starts from 0.
For example:
-
"2582"
is good because:- Index 0:
2
is even ✓ - Index 1:
5
is prime ✓ - Index 2:
8
is even ✓ - Index 3:
2
is prime ✓
- Index 0:
-
"3245"
is not good because:- Index 0:
3
is odd (should be even) ✗
- Index 0:
Given an integer n
, you need to find the total number of good digit strings of length n
. Since the result can be very large, return it modulo 10^9 + 7
.
The key insight is that for a string of length n
:
- There are
⌈n/2⌉
positions at even indices, each can be filled with 5 choices (0, 2, 4, 6, 8) - There are
⌊n/2⌋
positions at odd indices, each can be filled with 4 choices (2, 3, 5, 7)
Therefore, the total count is: 5^(⌈n/2⌉) × 4^(⌊n/2⌋) mod (10^9 + 7)
Intuition
When we look at this problem, we need to count valid combinations, not generate them. This is a classic combinatorics problem.
Let's think about building a good string position by position. For each position, we have independent choices that don't affect other positions:
- At position 0 (even index): We can choose from 5 even digits (0, 2, 4, 6, 8)
- At position 1 (odd index): We can choose from 4 prime digits (2, 3, 5, 7)
- At position 2 (even index): Again 5 choices
- At position 3 (odd index): Again 4 choices
- And so on...
Since each position's choice is independent, we can use the multiplication principle. If we have a string of length 4, the total number of good strings would be 5 × 4 × 5 × 4
.
We can see a pattern emerging:
- For length 1:
5
(only one even-indexed position) - For length 2:
5 × 4
- For length 3:
5 × 4 × 5 = 5² × 4
- For length 4:
5 × 4 × 5 × 4 = 5² × 4²
- For length 5:
5 × 4 × 5 × 4 × 5 = 5³ × 4²
The pattern shows that for a string of length n
:
- Number of even-indexed positions =
⌈n/2⌉
(ceiling of n/2) - Number of odd-indexed positions =
⌊n/2⌋
(floor of n/2)
Therefore, the total count becomes 5^(number of even positions) × 4^(number of odd positions)
.
Since the numbers can get very large, we use modular arithmetic and fast exponentiation to efficiently compute the result modulo 10^9 + 7
.
Solution Approach
The solution uses fast exponentiation to efficiently compute large powers modulo 10^9 + 7
.
Based on our analysis, we need to calculate:
Let's break down the implementation:
-
Calculate the number of even and odd positions:
- Even-indexed positions:
⌈n/2⌉ = ⌊(n+1)/2⌋
- Odd-indexed positions:
⌊n/2⌋
In the code, this is done using bit operations:
(n + 1) >> 1
is equivalent to⌊(n+1)/2⌋
(right shift by 1 divides by 2)n >> 1
is equivalent to⌊n/2⌋
- Even-indexed positions:
-
Use Python's built-in
pow
function with three arguments:pow(base, exponent, mod)
efficiently computesbase^exponent mod mod
using fast exponentiation- This handles large exponents efficiently in
O(log n)
time
-
Multiply the results:
- First compute
5^(⌈n/2⌉) mod (10^9 + 7)
- Then compute
4^(⌊n/2⌋) mod (10^9 + 7)
- Multiply them together and take modulo again to get the final answer
- First compute
The implementation:
def countGoodNumbers(self, n: int) -> int:
mod = 10**9 + 7
return pow(5, (n + 1) >> 1, mod) * pow(4, n >> 1, mod) % mod
This approach avoids overflow issues and runs in O(log n)
time complexity due to the fast exponentiation algorithm, making it efficient even for very large values of n
.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through the solution for n = 4
to see how we count good digit strings.
Step 1: Identify positions and their constraints For a string of length 4, we have positions [0, 1, 2, 3]:
- Position 0 (even index): must be even digit → choices: {0, 2, 4, 6, 8} = 5 options
- Position 1 (odd index): must be prime digit → choices: {2, 3, 5, 7} = 4 options
- Position 2 (even index): must be even digit → choices: {0, 2, 4, 6, 8} = 5 options
- Position 3 (odd index): must be prime digit → choices: {2, 3, 5, 7} = 4 options
Step 2: Count positions by type
- Even-indexed positions: positions 0 and 2 → count = 2 = ⌈4/2⌉
- Odd-indexed positions: positions 1 and 3 → count = 2 = ⌊4/2⌋
Step 3: Apply the multiplication principle Since each position's choice is independent:
- Total = (choices for even positions)^(count of even positions) × (choices for odd positions)^(count of odd positions)
- Total = 5² × 4² = 25 × 16 = 400
Step 4: Verify with code approach Using our formula with bit operations:
- Even positions:
(n + 1) >> 1 = (4 + 1) >> 1 = 5 >> 1 = 2
- Odd positions:
n >> 1 = 4 >> 1 = 2
- Result:
pow(5, 2, mod) * pow(4, 2, mod) % mod = 25 * 16 % mod = 400
Therefore, there are 400 good digit strings of length 4.
Some examples of valid strings: "0202", "2582", "4375", "8753" Example of invalid string: "1234" (position 0 has '1' which is odd, not even)
Solution Implementation
1class Solution:
2 def countGoodNumbers(self, n: int) -> int:
3 # Define the modulo constant for large number handling
4 MOD = 10**9 + 7
5
6 # Calculate the count of even positions (0, 2, 4, ...)
7 # For n digits, there are (n + 1) // 2 even positions
8 even_positions = (n + 1) >> 1 # Equivalent to (n + 1) // 2
9
10 # Calculate the count of odd positions (1, 3, 5, ...)
11 # For n digits, there are n // 2 odd positions
12 odd_positions = n >> 1 # Equivalent to n // 2
13
14 # Even positions can have 5 choices: 0, 2, 4, 6, 8
15 # Odd positions can have 4 choices: 2, 3, 5, 7 (prime digits)
16 # Use modular exponentiation to compute large powers efficiently
17 even_count = pow(5, even_positions, MOD)
18 odd_count = pow(4, odd_positions, MOD)
19
20 # Return the total count of good numbers
21 # Multiply the possibilities and apply modulo
22 return (even_count * odd_count) % MOD
23
1class Solution {
2 // Modulo value for preventing integer overflow
3 private final int MOD = 1000000007;
4
5 /**
6 * Counts the number of good digit strings of length n.
7 * A good digit string has:
8 * - Even indices (0, 2, 4, ...) containing even digits (0, 2, 4, 6, 8) - 5 choices
9 * - Odd indices (1, 3, 5, ...) containing prime digits (2, 3, 5, 7) - 4 choices
10 *
11 * @param n the length of the digit string
12 * @return the count of good digit strings modulo 10^9 + 7
13 */
14 public int countGoodNumbers(long n) {
15 // Calculate the number of even positions: ceiling of n/2 = (n+1)/2
16 long evenPositions = (n + 1) >> 1; // Equivalent to (n + 1) / 2
17
18 // Calculate the number of odd positions: floor of n/2 = n/2
19 long oddPositions = n >> 1; // Equivalent to n / 2
20
21 // Total count = 5^evenPositions * 4^oddPositions
22 // Use modular exponentiation to handle large numbers
23 long result = modularPower(5, evenPositions) * modularPower(4, oddPositions) % MOD;
24
25 return (int) result;
26 }
27
28 /**
29 * Calculates (base^exponent) % MOD using fast exponentiation.
30 * Time complexity: O(log n)
31 *
32 * @param base the base number
33 * @param exponent the power to raise the base to
34 * @return (base^exponent) % MOD
35 */
36 private long modularPower(long base, long exponent) {
37 long result = 1;
38
39 // Fast exponentiation algorithm
40 while (exponent != 0) {
41 // If exponent is odd, multiply result by base
42 if ((exponent & 1) == 1) {
43 result = result * base % MOD;
44 }
45
46 // Square the base for the next iteration
47 base = base * base % MOD;
48
49 // Divide exponent by 2
50 exponent >>= 1; // Equivalent to exponent / 2
51 }
52
53 return result;
54 }
55}
56
1class Solution {
2public:
3 int countGoodNumbers(long long n) {
4 const int MOD = 1000000007; // 10^9 + 7 for modular arithmetic
5
6 // Lambda function to calculate (base^exponent) % MOD using fast exponentiation
7 auto modularPower = [](long long base, long long exponent) -> long long {
8 long long result = 1;
9
10 // Binary exponentiation algorithm
11 while (exponent > 0) {
12 // If current bit is 1, multiply result with base
13 if ((exponent & 1) == 1) {
14 result = (result * base) % MOD;
15 }
16 // Square the base for next bit position
17 base = (base * base) % MOD;
18 // Right shift to process next bit
19 exponent >>= 1;
20 }
21
22 return result;
23 };
24
25 // Calculate the count of good numbers:
26 // - Even positions (0, 2, 4, ...) can have 5 choices (0, 2, 4, 6, 8)
27 // - Odd positions (1, 3, 5, ...) can have 4 choices (2, 3, 5, 7)
28 // - Number of even positions: (n + 1) / 2
29 // - Number of odd positions: n / 2
30 long long evenPositionCount = (n + 1) >> 1; // Equivalent to (n + 1) / 2
31 long long oddPositionCount = n >> 1; // Equivalent to n / 2
32
33 // Total count = 5^(even positions) * 4^(odd positions) % MOD
34 return (modularPower(5, evenPositionCount) * modularPower(4, oddPositionCount)) % MOD;
35 }
36};
37
1/**
2 * Counts the number of good digit strings of length n.
3 * A digit string is good if:
4 * - Digits at even indices (0-indexed) are even (0, 2, 4, 6, 8)
5 * - Digits at odd indices are prime (2, 3, 5, 7)
6 *
7 * @param n - The length of the digit string
8 * @returns The count of good numbers modulo 10^9 + 7
9 */
10function countGoodNumbers(n: number): number {
11 const MOD = 1000000007n;
12
13 /**
14 * Performs modular exponentiation using binary exponentiation.
15 * Calculates (base^exponent) % MOD efficiently.
16 *
17 * @param base - The base number
18 * @param exponent - The exponent
19 * @returns The result of (base^exponent) % MOD
20 */
21 const modularPower = (base: bigint, exponent: bigint): bigint => {
22 let result = 1n;
23
24 // Process each bit of the exponent
25 while (exponent > 0n) {
26 // If current bit is 1, multiply result by base
27 if (exponent & 1n) {
28 result = (result * base) % MOD;
29 }
30 // Square the base for the next bit position
31 base = (base * base) % MOD;
32 // Right shift to process next bit
33 exponent >>= 1n;
34 }
35
36 return result;
37 };
38
39 // Calculate number of positions with even indices (5 choices: 0,2,4,6,8)
40 // For n positions, there are ceiling(n/2) even positions
41 const evenPositionCount = BigInt(n + 1) / 2n;
42 const evenPositionCombinations = modularPower(5n, evenPositionCount);
43
44 // Calculate number of positions with odd indices (4 choices: 2,3,5,7)
45 // For n positions, there are floor(n/2) odd positions
46 const oddPositionCount = BigInt(n) / 2n;
47 const oddPositionCombinations = modularPower(4n, oddPositionCount);
48
49 // Total combinations = even position combinations * odd position combinations
50 const totalCombinations = (evenPositionCombinations * oddPositionCombinations) % MOD;
51
52 return Number(totalCombinations);
53}
54
Time and Space Complexity
The time complexity of this code is O(log n)
. This is because the built-in pow
function with three arguments uses fast exponentiation (also known as exponentiation by squaring) to compute the modular exponentiation. Each call to pow(base, exponent, mod)
takes O(log exponent)
time. In this code:
pow(5, (n + 1) >> 1, mod)
computes5^⌈n/2⌉ mod (10^9 + 7)
inO(log((n + 1) >> 1))
=O(log n)
timepow(4, n >> 1, mod)
computes4^⌊n/2⌋ mod (10^9 + 7)
inO(log(n >> 1))
=O(log n)
time
Since these operations are performed sequentially and then multiplied together, the overall time complexity remains O(log n)
.
The space complexity is O(1)
. The algorithm only uses a constant amount of extra space to store:
- The
mod
variable - Intermediate results from the
pow
function calls - The final result before returning
The fast exponentiation algorithm implemented in Python's built-in pow
function uses an iterative approach rather than recursive, so it doesn't consume additional stack space proportional to the input size.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Integer Overflow When Not Using Modular Arithmetic Properly
Pitfall: A common mistake is computing the powers first and then applying modulo at the end:
# WRONG approach - will cause overflow for large n result = (5 ** even_positions) * (4 ** odd_positions) % MOD
This fails because 5^even_positions
and 4^odd_positions
can become astronomically large before the modulo operation, causing memory errors or overflow.
Solution: Use modular exponentiation throughout the calculation:
# CORRECT approach
even_count = pow(5, even_positions, MOD)
odd_count = pow(4, odd_positions, MOD)
result = (even_count * odd_count) % MOD
2. Incorrect Position Count Calculation
Pitfall: Confusing the count of even and odd positions:
# WRONG - swapped the calculations even_positions = n // 2 odd_positions = (n + 1) // 2
For a string of length n:
- When n=1: only position 0 (even) exists
- When n=2: positions 0 (even) and 1 (odd) exist
- When n=3: positions 0, 2 (even) and 1 (odd) exist
Solution: Remember that even positions always equal or exceed odd positions:
# CORRECT even_positions = (n + 1) // 2 # Ceiling of n/2 odd_positions = n // 2 # Floor of n/2
3. Missing Final Modulo Operation
Pitfall: Forgetting to apply modulo after multiplication:
# WRONG - missing final modulo
return pow(5, even_positions, MOD) * pow(4, odd_positions, MOD)
Even though each pow()
returns a value less than MOD, their product can exceed MOD.
Solution: Always apply modulo after multiplication:
# CORRECT
return (pow(5, even_positions, MOD) * pow(4, odd_positions, MOD)) % MOD
4. Using Custom Power Function Instead of Built-in
Pitfall: Implementing your own power function with modulo:
# INEFFICIENT and error-prone
def power_mod(base, exp, mod):
result = 1
for _ in range(exp):
result = (result * base) % mod
return result
This has O(n) time complexity and is much slower for large exponents.
Solution: Use Python's built-in pow(base, exp, mod)
which implements fast exponentiation with O(log n) complexity:
# EFFICIENT
result = pow(base, exponent, MOD)
Which type of traversal does breadth first search do?
Recommended Readings
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
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
Coding Interview 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
Want a Structured Path to Master System Design Too? Don’t Miss This!