Facebook Pixel

1556. Thousand Separator

EasyString
Leetcode Link

Problem Description

Given an integer n, you need to format it by adding dots (".") as thousands separators and return the formatted number as a string.

The problem asks you to take any non-negative integer and insert dots every three digits from the right to make the number more readable. For example:

  • Input: 987 → Output: "987" (no separator needed)
  • Input: 1234 → Output: "1.234"
  • Input: 123456789 → Output: "123.456.789"
  • Input: 0 → Output: "0"

The solution works by extracting digits from right to left using division and modulo operations. It keeps a counter to track when to insert a dot (every 3 digits). The digits and dots are collected in a list, then reversed and joined to form the final string since we process the number from right to left but need to display it from left to right.

The key steps in the algorithm are:

  1. Extract the rightmost digit using divmod(n, 10)
  2. Add the digit to the result list
  3. Increment a counter
  4. If we've processed 3 digits and there are more digits to process, add a dot
  5. Continue until all digits are processed
  6. Reverse the result list and join into a string
Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

When we need to add thousands separators to a number, the natural way to think about it is from right to left - we place a dot after every group of 3 digits starting from the rightmost position. This mirrors how we naturally read large numbers: we group digits in threes from the right.

The challenge is that we need to process the number from right to left (to know where to place dots), but strings are typically built from left to right. This suggests we should extract digits from the right side of the number.

To extract digits from right to left, we can repeatedly use the modulo operation n % 10 to get the rightmost digit, and integer division n // 10 to remove that digit from the number. This is a standard technique for digit extraction.

As we extract each digit, we need to track how many consecutive digits we've processed without adding a separator. Once we hit 3 digits, we insert a dot and reset our counter. The key insight is that we only add a dot if there are more digits to process (when n > 0 after division), preventing unnecessary dots at the beginning of the number.

Since we're building the result from right to left but need to display it from left to right, we collect everything in a list and reverse it at the end. This is more efficient than string concatenation and gives us the correct left-to-right ordering.

The divmod(n, 10) function elegantly combines both operations we need - getting the quotient (remaining number) and remainder (current digit) in one step, making the code cleaner and slightly more efficient.

Solution Approach

The implementation uses a digit extraction approach with a counter to track when to insert separators.

We initialize two variables:

  • cnt = 0: A counter to track how many digits we've processed in the current group
  • ans = []: A list to store the digits and dots as we build the result

The main loop continues indefinitely with while 1: and processes the number digit by digit:

  1. Extract digit: Use divmod(n, 10) to simultaneously get:

    • n: The quotient (remaining number after removing the rightmost digit)
    • v: The remainder (the rightmost digit)
  2. Store digit: Convert the digit v to string and append to ans list

  3. Update counter: Increment cnt to track we've processed one more digit

  4. Check termination: If n == 0, we've processed all digits and break from the loop

  5. Insert separator: If cnt == 3 (we've collected 3 digits), we:

    • Append a dot '.' to the list
    • Reset cnt = 0 to start counting the next group

The crucial part is that we check for separator insertion after checking if n == 0. This ensures we don't add an unnecessary dot at the beginning of the number (which would appear at the end before reversal).

Finally, since we built the result from right to left, we reverse the list with ans[::-1] and join all elements into a string with ''.join().

For example, with input 1234:

  • First iteration: Extract 4, ans = ['4'], cnt = 1
  • Second iteration: Extract 3, ans = ['4', '3'], cnt = 2
  • Third iteration: Extract 2, ans = ['4', '3', '2'], cnt = 3, add dot: ans = ['4', '3', '2', '.'], reset cnt = 0
  • Fourth iteration: Extract 1, ans = ['4', '3', '2', '.', '1'], cnt = 1
  • n = 0, break
  • Reverse and join: '1.234'

Ready to land your dream job?

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

Start Evaluator

Example Walkthrough

Let's walk through the algorithm with the input n = 12345:

Initial State:

  • n = 12345
  • cnt = 0 (digit counter)
  • ans = [] (result list)

Iteration 1:

  • divmod(12345, 10)n = 1234, v = 5
  • Add '5' to ans: ans = ['5']
  • Increment counter: cnt = 1
  • Check if done: n = 1234 (not zero, continue)
  • Check separator: cnt = 1 (not 3, no separator needed)

Iteration 2:

  • divmod(1234, 10)n = 123, v = 4
  • Add '4' to ans: ans = ['5', '4']
  • Increment counter: cnt = 2
  • Check if done: n = 123 (not zero, continue)
  • Check separator: cnt = 2 (not 3, no separator needed)

Iteration 3:

  • divmod(123, 10)n = 12, v = 3
  • Add '3' to ans: ans = ['5', '4', '3']
  • Increment counter: cnt = 3
  • Check if done: n = 12 (not zero, continue)
  • Check separator: cnt = 3 (equals 3, add separator!)
  • Add dot and reset: ans = ['5', '4', '3', '.'], cnt = 0

Iteration 4:

  • divmod(12, 10)n = 1, v = 2
  • Add '2' to ans: ans = ['5', '4', '3', '.', '2']
  • Increment counter: cnt = 1
  • Check if done: n = 1 (not zero, continue)
  • Check separator: cnt = 1 (not 3, no separator needed)

Iteration 5:

  • divmod(1, 10)n = 0, v = 1
  • Add '1' to ans: ans = ['5', '4', '3', '.', '2', '1']
  • Increment counter: cnt = 2
  • Check if done: n = 0 (zero, exit loop!)

Final Step:

  • Reverse the list: ['1', '2', '.', '3', '4', '5']
  • Join into string: "12.345"

The key insight is that we process digits from right to left (5→4→3→2→1), inserting a dot after every third digit. By reversing at the end, we get the correct left-to-right representation with dots properly placed as thousands separators.

Solution Implementation

1class Solution:
2    def thousandSeparator(self, n: int) -> str:
3        """
4        Add thousand separators (dots) to an integer.
5      
6        Args:
7            n: A non-negative integer to format with thousand separators
8          
9        Returns:
10            A string representation of the number with dots as thousand separators
11        """
12        # Counter to track digits processed in current group (groups of 3)
13        digit_count = 0
14        # List to build the result string from right to left
15        result = []
16      
17        # Process digits from right to left
18        while True:
19            # Extract the rightmost digit using divmod
20            n, digit = divmod(n, 10)
21          
22            # Add the current digit to result
23            result.append(str(digit))
24            digit_count += 1
25          
26            # If no more digits left, exit the loop
27            if n == 0:
28                break
29          
30            # After every 3 digits, add a thousand separator
31            if digit_count == 3:
32                result.append('.')
33                digit_count = 0  # Reset counter for next group
34      
35        # Reverse the result since we built it backwards
36        return ''.join(result[::-1])
37
1class Solution {
2    /**
3     * Adds thousand separators (dots) to an integer number.
4     * For example: 1234567 becomes "1.234.567"
5     * 
6     * @param n The integer to format with thousand separators
7     * @return A string representation of the number with dots as thousand separators
8     */
9    public String thousandSeparator(int n) {
10        // Counter to track digits processed in current group (groups of 3)
11        int digitCount = 0;
12      
13        // StringBuilder to construct the result string efficiently
14        StringBuilder result = new StringBuilder();
15      
16        // Process digits from right to left
17        while (true) {
18            // Extract the rightmost digit
19            int currentDigit = n % 10;
20          
21            // Remove the rightmost digit from n
22            n /= 10;
23          
24            // Append the current digit to result (building in reverse)
25            result.append(currentDigit);
26          
27            // Increment the digit counter for current group
28            ++digitCount;
29          
30            // Check if all digits have been processed
31            if (n == 0) {
32                break;
33            }
34          
35            // Add separator after every 3 digits
36            if (digitCount == 3) {
37                result.append('.');
38                digitCount = 0;  // Reset counter for next group
39            }
40        }
41      
42        // Reverse the string since we built it backwards
43        return result.reverse().toString();
44    }
45}
46
1class Solution {
2public:
3    string thousandSeparator(int n) {
4        // Counter to track digits processed (for inserting separator every 3 digits)
5        int digitCount = 0;
6      
7        // Result string to build the formatted number
8        string result;
9      
10        // Process digits from right to left
11        while (true) {
12            // Extract the rightmost digit
13            int digit = n % 10;
14            n /= 10;
15          
16            // Append the digit to result (building string in reverse)
17            result += to_string(digit);
18          
19            // If all digits are processed, exit the loop
20            if (n == 0) {
21                break;
22            }
23          
24            // Increment digit counter and insert separator after every 3 digits
25            digitCount++;
26            if (digitCount == 3) {
27                result += '.';
28                digitCount = 0;  // Reset counter for next group
29            }
30        }
31      
32        // Reverse the string to get the correct order (since we built it backwards)
33        reverse(result.begin(), result.end());
34      
35        return result;
36    }
37};
38
1function thousandSeparator(n: number): string {
2    // Counter to track digits processed (for inserting separator every 3 digits)
3    let digitCount: number = 0;
4  
5    // Result string to build the formatted number
6    let result: string = "";
7  
8    // Process digits from right to left
9    while (true) {
10        // Extract the rightmost digit
11        const digit: number = n % 10;
12        n = Math.floor(n / 10);
13      
14        // Append the digit to result (building string in reverse)
15        result += digit.toString();
16      
17        // If all digits are processed, exit the loop
18        if (n === 0) {
19            break;
20        }
21      
22        // Increment digit counter and insert separator after every 3 digits
23        digitCount++;
24        if (digitCount === 3) {
25            result += '.';
26            digitCount = 0;  // Reset counter for next group
27        }
28    }
29  
30    // Reverse the string to get the correct order (since we built it backwards)
31    return result.split('').reverse().join('');
32}
33

Time and Space Complexity

Time Complexity: O(d) where d is the number of digits in the integer n.

The algorithm processes each digit of the number exactly once. In each iteration of the while loop:

  • divmod(n, 10) operation takes O(1) time
  • Appending to the list takes O(1) amortized time
  • String conversion and other operations are O(1)

Since the number of digits in n is ⌊log₁₀(n)⌋ + 1 for n > 0 (or 1 for n = 0), the loop runs d times. The final ans[::-1] reversal takes O(d) time, and ''.join() also takes O(d) time. Therefore, the overall time complexity is O(d) + O(d) + O(d) = O(d).

Space Complexity: O(d) where d is the number of digits in the integer n.

The space is used for:

  • The ans list which stores all digits plus separators. In the worst case, this contains approximately d + ⌊(d-1)/3⌋ elements (digits plus dots)
  • The reversed list created by ans[::-1] which has the same size
  • The final string created by ''.join() which has the same size

All these are proportional to the number of digits d, resulting in a space complexity of O(d).

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

Common Pitfalls

1. Adding Extra Separator at the Beginning

One of the most common mistakes is placing the separator check before the termination condition, which results in an extra dot at the beginning of numbers with digits that are multiples of 3.

Incorrect Implementation:

def thousandSeparator(self, n: int) -> str:
    digit_count = 0
    result = []
  
    while True:
        n, digit = divmod(n, 10)
        result.append(str(digit))
        digit_count += 1
      
        # Wrong: Checking separator BEFORE termination
        if digit_count == 3:
            result.append('.')
            digit_count = 0
          
        if n == 0:
            break
  
    return ''.join(result[::-1])

Problem: For input 123, this produces ".123" instead of "123" because we add a dot after processing the third digit even though there are no more digits left.

Solution: Always check if there are more digits to process (n == 0) before adding a separator. The correct order is:

  1. Process digit
  2. Check if we're done
  3. Only then add separator if needed

2. Handling Edge Case of Zero

Another pitfall is not properly handling when the input is 0. Some implementations might return an empty string or fail to process zero correctly.

Incorrect Approach:

def thousandSeparator(self, n: int) -> str:
    if n == 0:
        return ""  # Wrong: should return "0"
    # ... rest of the code

Solution: The algorithm should naturally handle 0 without special casing. When n = 0, the first iteration extracts the digit 0, adds it to the result, then breaks since n becomes 0.

3. Using String Concatenation Instead of List

Building the result using string concatenation in a loop is inefficient due to string immutability in Python.

Inefficient Implementation:

def thousandSeparator(self, n: int) -> str:
    result = ""  # Using string instead of list
    digit_count = 0
  
    while True:
        n, digit = divmod(n, 10)
        result = str(digit) + result  # String concatenation
        digit_count += 1
      
        if n == 0:
            break
          
        if digit_count == 3:
            result = "." + result  # More string concatenation
            digit_count = 0
  
    return result

Problem: Each string concatenation creates a new string object, leading to O(n²) time complexity where n is the number of digits.

Solution: Use a list to collect characters and join them at the end. This maintains O(n) time complexity.

4. Incorrect Counter Reset Logic

Forgetting to reset the counter after adding a separator or resetting it at the wrong time.

Incorrect:

if digit_count == 3:
    result.append('.')
    # Forgot to reset: digit_count = 0

This would cause dots to be placed incorrectly after the first group of three digits.

Solution: Always reset the counter immediately after adding a separator to start counting the next group of three digits.

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

Which data structure is used to implement priority queue?


Recommended Readings

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

Load More