Facebook Pixel

2117. Abbreviating the Product of a Range

Problem Description

You need to find the product of all integers in the range [left, right] (inclusive) and represent it in an abbreviated format.

The abbreviation process works as follows:

  1. Remove trailing zeros: Calculate the product, then count and remove all trailing zeros. Store this count as C.

    • For example, 1000 has 3 trailing zeros, while 546 has 0 trailing zeros.
  2. Format the remaining digits: After removing trailing zeros, count the remaining digits as d.

    • If d > 10: Show only the first 5 digits and last 5 digits, separated by ...
      • Example: 1234567654321 becomes 12345...54321
    • If d ≤ 10: Keep the number as is
      • Example: 1234567 stays as 1234567
  3. Final representation: Express the result as a string in the format <number>eC

    • Example: 12345678987600000 becomes "12345...89876e5" (5 trailing zeros removed)

The solution approach cleverly handles the large product by:

  • Counting factors of 2 and 5 separately to determine trailing zeros (since 10 = 2 × 5)
  • Maintaining both prefix and suffix of the product to avoid overflow
  • Removing factors of 2 and 5 during multiplication to keep numbers manageable
  • Using modulo operations to track the last digits while keeping track of whether the product exceeds 10 digits

The key insight is that trailing zeros come from pairs of factors 2 and 5, so C = min(count of 2s, count of 5s). The algorithm tracks the first 5 and last 5 significant digits separately to handle very large products efficiently.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

The main challenge is that the product of a range of numbers can become astronomically large, making it impossible to compute directly. We need to think about what information we actually need to track.

First, let's understand where trailing zeros come from. A trailing zero appears when we multiply by 10, and 10 = 2 × 5. So each trailing zero corresponds to a pair of factors 2 and 5 in our product. Since 2s appear more frequently than 5s in most ranges, the number of trailing zeros equals the minimum of the count of 2s and count of 5s.

Now, how do we handle the massive product? The key insight is that we don't need the entire number - we only need:

  • The first 5 digits (for the prefix)
  • The last 5 digits (for the suffix)
  • The count of trailing zeros

For the prefix, we can multiply numbers normally and keep dividing by 10 whenever the result gets too large (beyond 5 digits). This maintains the most significant digits.

For the suffix, we need to be more careful. We want the last 5 non-zero digits. The trick is to:

  1. Remove factors of 2 and 5 as we multiply (since they contribute to trailing zeros, not the significant digits)
  2. Keep only the last 10 digits using modulo 10^10 to prevent overflow
  3. At the end, take modulo 10^5 to get the last 5 significant digits

Why remove 2s and 5s during multiplication? If we kept them, our number would grow unnecessarily large with zeros that we'd eventually discard anyway. By removing equal numbers of 2s and 5s (which form 10s), we're essentially doing the "remove trailing zeros" step incrementally during the computation rather than at the end.

The gt (greater than) flag tracks whether our product ever exceeded 10 digits. If it never did, we don't need the abbreviated format with ... - we can just return the actual product with trailing zeros removed.

Learn more about Math patterns.

Solution Approach

The implementation follows these key steps:

Step 1: Count factors of 2 and 5

cnt2 = cnt5 = 0
for x in range(left, right + 1):
    while x % 2 == 0:
        cnt2 += 1
        x //= 2
    while x % 5 == 0:
        cnt5 += 1
        x //= 5

We iterate through each number in the range and count total factors of 2 and 5. This determines how many trailing zeros the final product will have.

Step 2: Initialize variables

c = cnt2 = cnt5 = min(cnt2, cnt5)
pre = suf = 1
gt = False
  • c stores the number of trailing zeros (minimum of 2s and 5s count)
  • We reset cnt2 and cnt5 to this minimum value - we'll decrement these as we remove factors
  • pre tracks the prefix (first 5 digits)
  • suf tracks the suffix (last significant digits)
  • gt flag indicates if the product exceeds 10 digits

Step 3: Calculate prefix and suffix

for x in range(left, right + 1):
    suf *= x
    while cnt2 and suf % 2 == 0:
        suf //= 2
        cnt2 -= 1
    while cnt5 and suf % 5 == 0:
        suf //= 5
        cnt5 -= 1
    if suf >= 1e10:
        gt = True
        suf %= int(1e10)
    pre *= x
    while pre > 1e5:
        pre /= 10

For each number:

  • Suffix calculation: Multiply suf by the current number, then remove pairs of 2s and 5s (which form trailing zeros). We only remove as many as we counted (cnt2 and cnt5 act as counters). Keep only the last 10 digits using modulo to prevent overflow.
  • Prefix calculation: Multiply pre by the current number. Whenever it exceeds 5 digits, divide by 10 to maintain only the most significant 5 digits.
  • Set gt = True if suffix ever reaches or exceeds 10^10, indicating the product has more than 10 significant digits.

Step 4: Format the result

if gt:
    return str(int(pre)) + "..." + str(suf % int(1e5)).zfill(5) + "e" + str(c)
return str(suf) + "e" + str(c)
  • If the product exceeded 10 digits (gt == True): Return the abbreviated format with first 5 digits, "...", last 5 digits (using zfill(5) to ensure 5 digits with leading zeros if needed), and the exponent notation.
  • Otherwise: Return the actual product (with trailing zeros removed) followed by the exponent notation.

The clever part of this algorithm is maintaining both prefix and suffix simultaneously while removing trailing zero factors on-the-fly, allowing us to handle products that would otherwise cause integer overflow.

Ready to land your dream job?

Unlock your dream job with a 3-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through the solution with the range [12, 19].

Step 1: Count factors of 2 and 5

Going through each number to count prime factors:

  • 12 = 2² × 3 → contributes 2 twos
  • 13 = 13 → contributes 0 twos, 0 fives
  • 14 = 2 × 7 → contributes 1 two
  • 15 = 3 × 5 → contributes 1 five
  • 16 = 2⁴ → contributes 4 twos
  • 17 = 17 → contributes 0 twos, 0 fives
  • 18 = 2 × 3² → contributes 1 two
  • 19 = 19 → contributes 0 twos, 0 fives

Total: cnt2 = 8, cnt5 = 1 Trailing zeros: c = min(8, 1) = 1

Step 2: Initialize variables

  • c = 1 (one trailing zero)
  • cnt2 = 1, cnt5 = 1 (reset to minimum, will decrement as we remove factors)
  • pre = 1, suf = 1 (start products)
  • gt = False (hasn't exceeded 10 digits yet)

Step 3: Calculate prefix and suffix

Processing each number:

xsuf beforesuf after multiplyRemove 2s/5ssuf finalpre
1211212÷2=6 (cnt2→0)612
13678no factors78156
14781092no 2s left10922184
1510921638016380÷5=3276 (cnt5→0)327632760
16327652416no factors left5241652416
1752416891072no factors left89107289107 (÷10)
1889107216039296no factors left1603929616039 (÷100)
1916039296304746624no factors left30474662430474 (÷10)

Note: When pre > 100000, we divide by 10 to keep only 5 most significant digits.

The actual product is 12×13×14×15×16×17×18×19 = 304,746,240 (with one trailing zero from the 2×5 pair).

Step 4: Format the result

Since suf = 304746624 never exceeded 10¹⁰ and has 9 digits (less than 10), gt remains False.

After removing the trailing zero factor (one pair of 2×5), we had suf = 30474624. Since gt = False, we return: "30474624e1"

This matches our expectation: the product 304,746,240 has 1 trailing zero, and after removing it we get 30,474,624.


Another example with abbreviation: [25, 30]

Quick calculation shows this product is 25×26×27×28×29×30 = 427,518,000.

  • Factors: 25=5², 26=2×13, 27=3³, 28=2²×7, 29=29, 30=2×3×5
  • Total: cnt2 = 4, cnt5 = 3, so c = 3 trailing zeros
  • After removing 3 pairs of (2×5), the significant part is 427,518
  • Since this has only 6 digits (≤10), no abbreviation needed
  • Result: "427518e3"

For a range that would produce abbreviation (like [2, 100]), the product would have many more than 10 significant digits, triggering the gt flag and producing a result like "28242...28864e24".

Solution Implementation

1class Solution:
2    def abbreviateProduct(self, left: int, right: int) -> str:
3        # Count total factors of 2 and 5 in the product range
4        count_twos = count_fives = 0
5        for num in range(left, right + 1):
6            temp = num
7            # Count factors of 2
8            while temp % 2 == 0:
9                count_twos += 1
10                temp //= 2
11            # Count factors of 5
12            while temp % 5 == 0:
13                count_fives += 1
14                temp //= 5
15      
16        # Number of trailing zeros equals minimum of 2s and 5s
17        trailing_zeros = min(count_twos, count_fives)
18      
19        # Reset counters to remove equal pairs of 2s and 5s
20        remaining_twos = remaining_fives = trailing_zeros
21      
22        # Track prefix (first digits) and suffix (last digits)
23        prefix = 1
24        suffix = 1
25        overflow_occurred = False
26      
27        # Calculate the product while removing factors of 10
28        for num in range(left, right + 1):
29            # Update suffix with current number
30            suffix *= num
31          
32            # Remove factors of 2 up to the number of trailing zeros
33            while remaining_twos > 0 and suffix % 2 == 0:
34                suffix //= 2
35                remaining_twos -= 1
36          
37            # Remove factors of 5 up to the number of trailing zeros
38            while remaining_fives > 0 and suffix % 5 == 0:
39                suffix //= 5
40                remaining_fives -= 1
41          
42            # Check if suffix exceeds 10^10 (overflow detection)
43            if suffix >= 10**10:
44                overflow_occurred = True
45                suffix %= 10**10  # Keep last 10 digits
46          
47            # Update prefix with current number
48            prefix *= num
49          
50            # Keep prefix around 5 digits by dividing by 10
51            while prefix > 10**5:
52                prefix /= 10
53      
54        # Format the result based on whether overflow occurred
55        if overflow_occurred:
56            # Format: first_5_digits...last_5_digits_e_trailing_zeros
57            first_digits = str(int(prefix))
58            last_digits = str(suffix % 10**5).zfill(5)
59            return f"{first_digits}...{last_digits}e{trailing_zeros}"
60        else:
61            # Format: exact_value_e_trailing_zeros
62            return f"{suffix}e{trailing_zeros}"
63
1class Solution {
2  
3    public String abbreviateProduct(int left, int right) {
4        // Count factors of 2 and 5 in the product range
5        int countFactorsOf2 = 0;
6        int countFactorsOf5 = 0;
7      
8        // Extract all factors of 2 and 5 from each number in range
9        for (int num = left; num <= right; num++) {
10            int temp = num;
11          
12            // Count factors of 2
13            while (temp % 2 == 0) {
14                countFactorsOf2++;
15                temp /= 2;
16            }
17          
18            // Count factors of 5
19            while (temp % 5 == 0) {
20                countFactorsOf5++;
21                temp /= 5;
22            }
23        }
24      
25        // The number of trailing zeros equals min(count of 2s, count of 5s)
26        int trailingZeros = Math.min(countFactorsOf2, countFactorsOf5);
27      
28        // Remove paired factors of 2 and 5 (they create trailing zeros)
29        int remainingFactorsOf2 = trailingZeros;
30        int remainingFactorsOf5 = trailingZeros;
31      
32        // Track the suffix (last digits) and prefix (first digits) of the product
33        long suffixDigits = 1;
34        double prefixDigits = 1;
35        boolean hasOverflow = false;
36      
37        // Calculate the product while removing factors of 2 and 5
38        for (int num = left; num <= right; num++) {
39            // Multiply suffix by current number
40            suffixDigits *= num;
41          
42            // Remove factors of 2 from suffix
43            while (remainingFactorsOf2 > 0 && suffixDigits % 2 == 0) {
44                suffixDigits /= 2;
45                remainingFactorsOf2--;
46            }
47          
48            // Remove factors of 5 from suffix
49            while (remainingFactorsOf5 > 0 && suffixDigits % 5 == 0) {
50                suffixDigits /= 5;
51                remainingFactorsOf5--;
52            }
53          
54            // Check if product exceeds 10^10 (need abbreviation)
55            if (suffixDigits >= (long) 1e10) {
56                hasOverflow = true;
57                // Keep only last 10 digits
58                suffixDigits %= (long) 1e10;
59            }
60          
61            // Calculate prefix digits (first 5 digits)
62            prefixDigits *= num;
63            // Keep prefix manageable by dividing when it exceeds 10^5
64            while (prefixDigits > 1e5) {
65                prefixDigits /= 10;
66            }
67        }
68      
69        // Format the result based on whether abbreviation is needed
70        if (hasOverflow) {
71            // Format: [first 5 digits]...[last 5 digits]e[trailing zeros]
72            int firstFiveDigits = (int) prefixDigits;
73            int lastFiveDigits = (int) (suffixDigits % (int) 1e5);
74            return firstFiveDigits + "..." + String.format("%05d", lastFiveDigits) + "e" + trailingZeros;
75        } else {
76            // No abbreviation needed, just append trailing zeros count
77            return suffixDigits + "e" + trailingZeros;
78        }
79    }
80}
81
1class Solution {
2public:
3    string abbreviateProduct(int left, int right) {
4        // Count total factors of 2 and 5 in the product range
5        int factorsOfTwo = 0, factorsOfFive = 0;
6        for (int num = left; num <= right; ++num) {
7            int temp = num;
8            // Count factors of 2 in current number
9            while (temp % 2 == 0) {
10                ++factorsOfTwo;
11                temp /= 2;
12            }
13            // Count factors of 5 in current number
14            while (temp % 5 == 0) {
15                ++factorsOfFive;
16                temp /= 5;
17            }
18        }
19      
20        // Number of trailing zeros equals min(factors of 2, factors of 5)
21        int trailingZeros = min(factorsOfTwo, factorsOfFive);
22        factorsOfTwo = factorsOfFive = trailingZeros;
23      
24        // Track last significant digits (suffix) and first significant digits (prefix)
25        long long suffix = 1;
26        long double prefix = 1;
27        bool hasOverflow = false;
28      
29        // Calculate the product while removing trailing zeros
30        for (int num = left; num <= right; ++num) {
31            suffix *= num;
32          
33            // Remove factors of 2 from suffix
34            while (factorsOfTwo > 0 && suffix % 2 == 0) {
35                suffix /= 2;
36                --factorsOfTwo;
37            }
38          
39            // Remove factors of 5 from suffix
40            while (factorsOfFive > 0 && suffix % 5 == 0) {
41                suffix /= 5;
42                --factorsOfFive;
43            }
44          
45            // Keep only last 10 digits if overflow occurs
46            if (suffix >= 1e10) {
47                hasOverflow = true;
48                suffix %= (long long)1e10;
49            }
50          
51            // Maintain prefix at most 5 significant digits
52            prefix *= num;
53            while (prefix > 1e5) {
54                prefix /= 10;
55            }
56        }
57      
58        // Format output based on whether overflow occurred
59        if (hasOverflow) {
60            // Product is too large: show first 5 digits...last 5 digits
61            char buffer[10];
62            snprintf(buffer, sizeof(buffer), "%05lld", suffix % (int)1e5);
63            return to_string((int)prefix) + "..." + string(buffer) + "e" + to_string(trailingZeros);
64        }
65      
66        // Product fits: show complete non-zero part
67        return to_string(suffix) + "e" + to_string(trailingZeros);
68    }
69};
70
1function abbreviateProduct(left: number, right: number): string {
2    // Count total factors of 2 and 5 in the product range
3    let factorsOfTwo = 0;
4    let factorsOfFive = 0;
5  
6    for (let num = left; num <= right; num++) {
7        let temp = num;
8      
9        // Count factors of 2 in current number
10        while (temp % 2 === 0) {
11            factorsOfTwo++;
12            temp = Math.floor(temp / 2);
13        }
14      
15        // Count factors of 5 in current number
16        while (temp % 5 === 0) {
17            factorsOfFive++;
18            temp = Math.floor(temp / 5);
19        }
20    }
21  
22    // Number of trailing zeros equals min(factors of 2, factors of 5)
23    const trailingZeros = Math.min(factorsOfTwo, factorsOfFive);
24    factorsOfTwo = trailingZeros;
25    factorsOfFive = trailingZeros;
26  
27    // Track last significant digits (suffix) and first significant digits (prefix)
28    let suffix = 1;
29    let prefix = 1;
30    let hasOverflow = false;
31  
32    // Calculate the product while removing trailing zeros
33    for (let num = left; num <= right; num++) {
34        suffix *= num;
35      
36        // Remove factors of 2 from suffix
37        while (factorsOfTwo > 0 && suffix % 2 === 0) {
38            suffix = Math.floor(suffix / 2);
39            factorsOfTwo--;
40        }
41      
42        // Remove factors of 5 from suffix
43        while (factorsOfFive > 0 && suffix % 5 === 0) {
44            suffix = Math.floor(suffix / 5);
45            factorsOfFive--;
46        }
47      
48        // Keep only last 10 digits if overflow occurs
49        if (suffix >= 1e10) {
50            hasOverflow = true;
51            suffix = suffix % 1e10;
52        }
53      
54        // Maintain prefix at most 5 significant digits
55        prefix *= num;
56        while (prefix > 1e5) {
57            prefix = prefix / 10;
58        }
59    }
60  
61    // Format output based on whether overflow occurred
62    if (hasOverflow) {
63        // Product is too large: show first 5 digits...last 5 digits
64        const last5Digits = (suffix % 1e5).toString().padStart(5, '0');
65        const first5Digits = Math.floor(prefix).toString();
66        return `${first5Digits}...${last5Digits}e${trailingZeros}`;
67    }
68  
69    // Product fits: show complete non-zero part
70    return `${suffix}e${trailingZeros}`;
71}
72

Time and Space Complexity

Time Complexity: O(n * log(max_value))

The algorithm consists of two main passes through the range [left, right]:

  1. First loop: Counts factors of 2 and 5 in each number from left to right. For each number x, the while loops extract all factors of 2 and 5. In the worst case, a number could have O(log x) factors of 2 or 5. Since we iterate through n = right - left + 1 numbers, this takes O(n * log(max_value)) time.

  2. Second loop: Iterates through all numbers from left to right again, performing:

    • Multiplication operations
    • Division operations to remove factors of 2 and 5 (bounded by the total count from the first loop)
    • Division operations to keep pre within bounds (at most O(log x) divisions per number)

    This loop also takes O(n * log(max_value)) time.

Overall time complexity: O(n * log(max_value)) where n = right - left + 1 and max_value is the maximum value in the range.

Space Complexity: O(1)

The algorithm uses only a constant amount of extra space:

  • Integer variables: cnt2, cnt5, c, x
  • Numeric variables: pre, suf
  • Boolean variable: gt

All variables store single values regardless of the input size, resulting in O(1) space complexity.

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Integer Overflow When Computing the Product

Pitfall: Directly calculating the product of all numbers in the range will cause integer overflow for large ranges. For example, 100! ≈ 9.3 × 10^157, which far exceeds the maximum integer limit in most programming languages.

Solution: The algorithm avoids this by:

  • Tracking prefix and suffix separately instead of computing the full product
  • Removing factors of 2 and 5 immediately during multiplication to keep numbers smaller
  • Using modulo operations to maintain only the necessary digits

2. Incorrect Handling of Suffix Leading Zeros

Pitfall: When the suffix has leading zeros after taking modulo, they might be lost. For example, if the last 5 significant digits are 00123, simply converting to string would give "123" instead of "00123".

Solution: Use zfill(5) when formatting the suffix to ensure exactly 5 digits with leading zeros if necessary:

last_digits = str(suffix % 10**5).zfill(5)  # Ensures "00123" not "123"

3. Premature Factor Removal

Pitfall: Removing ALL factors of 2 and 5 from each number individually would lose information about the actual product. We need to remove only as many factors as there are trailing zeros.

Solution: Use counters (remaining_twos and remaining_fives) initialized to the minimum count, and decrement them as factors are removed:

while remaining_twos > 0 and suffix % 2 == 0:
    suffix //= 2
    remaining_twos -= 1

4. Floating Point Precision Loss in Prefix

Pitfall: Using floating-point division for the prefix (prefix /= 10) can accumulate rounding errors over many iterations, potentially giving incorrect first digits.

Solution: For better precision, consider using integer arithmetic with explicit tracking of the scale, or accept small rounding errors for the first few digits (which is usually acceptable since we only need 5 significant digits).

5. Incorrect Overflow Detection

Pitfall: Only checking if the final result exceeds 10 digits is insufficient. The product might exceed 10 digits during intermediate calculations but then be reduced by factor removal.

Solution: Set the overflow flag (gt or overflow_occurred) as soon as the suffix reaches 10^10, and never unset it:

if suffix >= 10**10:
    overflow_occurred = True  # Once set, stays True
    suffix %= 10**10

6. Off-by-One Errors in Range

Pitfall: Forgetting that the range [left, right] is inclusive on both ends, leading to missing the last number.

Solution: Always use range(left, right + 1) to include both endpoints in the calculation.

Discover Your Strengths and Weaknesses: Take Our 3-Minute Quiz to Tailor Your Study Plan:

A heap is a ...?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More