Thousand Separator: String Building from Right to Left
Given an integer n, add a dot "." as the thousands separator and return the result as a string. For example, n = 987654321 becomes "987.654.321", and n = 1234 becomes "1.234".
This is LeetCode 1556: Thousand Separator, an easy problem that tests your ability to build a string by processing digits from right to left. The core idea is simple: group every three digits starting from the ones place, then join those groups with dots.
Why this problem matters
Formatting numbers for display is something you will encounter in nearly every production codebase. Whether it is rendering prices, population counts, or file sizes, the "insert a separator every three digits" pattern is universal. This problem isolates that pattern and asks you to implement it from scratch.
Beyond the practical utility, Thousand Separator drills the skill of right-to-left string construction. Many string problems require you to build results from the end rather than the beginning: adding numbers digit by digit, reversing words, or formatting output with position-dependent separators. Getting comfortable with this direction of traversal is a fundamental skill.
The key insight: process from right to left
The separator goes between every group of three digits, counted from the right. If you try to work from left to right, you need to know the total length first to figure out where the first group boundary falls. That works, but it adds complexity.
A cleaner approach is to work from the right side of the string. Walk backward through the digits, and every time you have placed three digits, insert a dot. This way, the grouping logic is consistent regardless of how many digits the number has.
Think of it like reading a number aloud. You naturally group from the right: "nine hundred eighty-seven million, six hundred fifty-four thousand, three hundred twenty-one." The dots go exactly where those spoken group boundaries are.
The solution
def thousandSeparator(n):
s = str(n)
result = []
for i, ch in enumerate(reversed(s)):
if i > 0 and i % 3 == 0:
result.append(".")
result.append(ch)
return "".join(reversed(result))
Here is what each piece does:
- Convert
nto its string representation. This lets you iterate over individual digits. - Walk through the string in reverse (from the ones place to the highest digit).
- Every time you have accumulated three digits (when
i > 0 and i % 3 == 0), insert a dot before placing the next digit. - Since you built the result backward, reverse it at the end to get the correct left-to-right order.
The i > 0 check prevents adding a dot at the very start. The modulo check i % 3 == 0 triggers at positions 3, 6, 9, and so on, which are exactly the group boundaries.
Walking through it step by step
Let's trace through n = 987654321 to see how the algorithm groups digits and inserts separators.
Step 1: Convert to string, start from the right
Convert 987654321 to the string "987654321". We will scan from the rightmost digit and group every 3 digits.
Step 2: Group 1 (from right): "321"
Take the last 3 digits: "321". This is the first group. No dot needed yet.
Step 3: Group 2: "654", add dot separator
Take the next 3 digits: "654". Insert a dot between this group and the previous one. Running result: "654.321".
Step 4: Group 3: "987", add dot separator
Take the remaining digits: "987". Insert another dot. Running result: "987.654.321".
Step 5: Final result: "987.654.321"
All digits are grouped. The final output is "987.654.321". Three groups of three digits, separated by dots.
The algorithm processes each digit exactly once and inserts a dot at every third position from the right. No matter how many digits the number has, the grouping logic stays the same.
Complexity analysis
| Metric | Value |
|---|---|
| Time | O(d), where d is the number of digits in n |
| Space | O(d), for the result string |
This is optimal. You need to examine every digit at least once, and the output itself is O(d) characters long. There is no way to produce the result without at least O(d) work.
Building blocks
This problem is built on two reusable patterns that CodeBricks drills independently.
1. Right-to-left string building
The pattern of constructing a result by iterating from the end of a string toward the beginning:
result = []
for i, ch in enumerate(reversed(s)):
process(ch)
result.append(ch)
return "".join(reversed(result))
In Thousand Separator, you walk backward to group digits in threes. In Add Strings, you walk backward to add digits with carry propagation. In problems like reversing words in a sentence, you process tokens from the end. The skeleton is the same: iterate in reverse, build a result, then reverse the output at the end.
2. Digit grouping pattern
The pattern of using a counter with modular arithmetic to partition elements into fixed-size groups:
for i, item in enumerate(sequence):
if i > 0 and i % group_size == 0:
insert_separator()
process(item)
In Thousand Separator, the group size is 3 and the separator is a dot. In problems like formatting binary numbers, the group size might be 4 or 8. In pagination, the group size is the page length. The idea is the same: use i % k == 0 to detect group boundaries and insert the appropriate delimiter.
An alternative approach is to use Python's string slicing to extract chunks of three from the right. Both approaches are O(d), but the reverse-iteration approach generalizes better to languages without convenient slicing.
Edge cases
Before submitting, make sure your solution handles these:
- n = 0: The output is
"0". A single digit with no separators needed. - Single digit (n = 7): The output is
"7". No groups to separate. - Exact multiple of 1000 (n = 1000): The output is
"1.000". The trailing zeros are part of the number and must stay. - Very large number (n = 2147483647): The output is
"2.147.483.647". Four groups, with the leftmost group having just one digit. Your code should handle partial leftmost groups naturally.
The reverse-iteration approach handles all of these without special-case logic. When the number of digits is not a multiple of three, the leftmost group simply has fewer than three digits, and the i > 0 guard prevents a leading dot.
From understanding to recall
You have read the solution and it makes sense. Reverse the string, insert a dot every three characters, reverse again. Clean. But can you write it from scratch in a timed setting without looking at it?
The details matter: remembering the i > 0 check, getting the modulo condition right, and reversing the result at the end. These are small but easy to get wrong under pressure if you have not practiced writing them from memory.
Spaced repetition closes that gap. You practice writing the right-to-left grouping loop from scratch at increasing intervals. After a few rounds, the pattern is automatic. You see "insert separators every k positions from the right" and the code flows out without hesitation.
The right-to-left string building pattern is one of roughly 60 reusable building blocks that cover hundreds of LeetCode problems. Learning them individually and drilling them with spaced repetition is far more effective than grinding random problems and hoping they stick.
Related posts
- Add Strings - Another right-to-left string building problem where you process digits from the ones place
- Reverse String - The simplest form of reverse iteration on a string
- Integer to English Words - A harder number formatting problem that also groups digits in threes
CodeBricks breaks the thousand separator LeetCode problem into its right-to-left string building and digit grouping building blocks, then drills them independently with spaced repetition. You type each piece from scratch until the pattern is automatic. When a string formatting question shows up in your interview, you do not think about it. You just write it.