Skip to content
← All posts

Reformat Phone Number: String Grouping Pattern

5 min read
leetcodeproblemeasystrings

You are given a phone number as a string that contains digits, spaces, and dashes. Your task is to reformat it by first removing all spaces and dashes, then grouping the digits. Groups of 3 are preferred, but the last block must never be a single digit. If 4 digits remain at the end, split them into two groups of 2. Finally, join the groups with dashes.

This is LeetCode 1694: Reformat Phone Number, an easy problem that tests your ability to process strings methodically. The grouping logic is simple once you see the pattern, but getting the edge cases right requires careful handling of the tail end of the digit string.

input string1-23 456 78-9 0digits only10213243546576879809grouped output123-456-789-0
Input: "1-23 456 78-9 0". Strip non-digits to get "1234567890" (green). Group into blocks of 3, with the last digit as a leftover handled specially. Red = removed characters, yellow = dashes in output.

Why this problem matters

Reformat Phone Number is a practical string manipulation problem. In real-world applications, you constantly normalize user input: phone numbers, credit cards, serial numbers. The core skill is stripping unwanted characters and then chunking what remains into fixed-size blocks with special rules for the last chunk.

This same grouping pattern appears whenever you need to partition a sequence into fixed-size pieces with a constraint on the final piece. Learning to handle the "leftover" digits cleanly is the real lesson here.

The key insight

After stripping non-digit characters, you have a clean string of digits. From here, the algorithm is:

  1. Take groups of 3 from the left as long as 4 or more digits remain.
  2. When you are down to 2 or 3 remaining digits, they form the last group as-is.
  3. When you are down to 4 remaining digits, split them into two groups of 2 (because a group of 3 + a group of 1 is not allowed).

The critical rule is: never leave a single digit by itself. That is the only tricky part. Everything else is a simple loop.

The solution

def reformatNumber(number):
    digits = number.replace(" ", "").replace("-", "")
    groups = []
    i = 0
    while len(digits) - i > 4:
        groups.append(digits[i:i+3])
        i += 3
    remaining = len(digits) - i
    if remaining == 4:
        groups.append(digits[i:i+2])
        groups.append(digits[i+2:i+4])
    else:
        groups.append(digits[i:])
    return "-".join(groups)

Here is how each part works:

  1. digits = number.replace(" ", "").replace("-", "") strips all spaces and dashes, leaving only digits.
  2. The while loop takes groups of 3 as long as more than 4 digits remain. The condition len(digits) - i > 4 ensures we stop before we accidentally leave just 1 digit.
  3. After the loop, remaining is 2, 3, or 4. If it is 4, we split into two groups of 2. Otherwise, we take the remaining 2 or 3 digits as a single group.
  4. "-".join(groups) produces the final formatted string.

The condition > 4 (not >= 4) is the key. When exactly 4 digits remain, you do not take a group of 3 (which would leave 1). Instead you split into 2 + 2.

Visual walkthrough

Step 1: Strip all non-digit characters from the input.

input1-23 456digits123456

Remove spaces and dashes. Only keep characters 0-9. Input "1-23 456" becomes "123456".

Step 2: Group digits into blocks of 3 from the left.

digits1234567890groups so far123456789remaining0

Take groups of 3 as long as 4 or more digits remain. After taking three groups of 3, one digit remains.

Step 3: Handle the remaining digits. If 2 or 3 remain, they form one group. If 4 remain, split into two groups of 2.

case: 1 remaining (impossible alone)0fix: borrow from last group7890

You never leave just 1 digit. If 1 remains, pop the last group of 3 and redistribute those 4 digits as two groups of 2: "789" + "0" becomes "78" + "90".

Step 4: Join all groups with dashes to produce the final result.

final groups1234567890result123-456-78-90

Join with "-". Final answer: "123-456-78-90".

The walkthrough uses the input "1-23 456 78-9 0" which has 10 digits after stripping. The loop takes three groups of 3, leaving 1 digit. Since 1 alone is not valid, we back up: the last group of 3 and the leftover digit (4 digits total) get split into two groups of 2. The result is "123-456-78-90".

Complexity analysis

ApproachTimeSpace
Linear scanO(n)O(n)

The time is O(n) because you scan through the string once to strip characters and once more to form groups. The space is O(n) for storing the cleaned digit string and the output groups. There is no way to avoid O(n) space since the problem asks you to return a new string.

The building blocks

1. Character filtering

digits = number.replace(" ", "").replace("-", "")

Stripping unwanted characters from a string is one of the most common string operations. You can also use a list comprehension with isdigit(), but chained replace calls are cleaner when you know exactly which characters to remove.

2. Fixed-size chunking with tail handling

while len(data) - i > threshold:
    groups.append(data[i:i+chunk_size])
    i += chunk_size

This pattern appears whenever you need to split a sequence into fixed-size blocks. The trick is choosing the right stopping condition so the leftover is always a valid size. In this problem, the threshold is 4 and the chunk size is 3. In other problems (like Base64 encoding or packet framing), the numbers change but the structure is identical.

Edge cases

Before submitting, make sure your solution handles these:

  • Exactly 2 digits "1 2": after stripping, "12". One group of 2. Returns "12".
  • Exactly 3 digits "1-2-3": after stripping, "123". One group of 3. Returns "123".
  • Exactly 4 digits "1 2 3 4": after stripping, "1234". Two groups of 2. Returns "12-34".
  • Long string with no spaces or dashes "123456789": 9 digits, three groups of 3. Returns "123-456-789".
  • All spaces and dashes between single digits "1 - 2 - 3 - 4 - 5": stripping produces "12345". Group as "123-45".
  • Trailing spaces "12 --3 ": stripping produces "123". Returns "123".

The algorithm handles all of these without special-case logic because the while loop and the remaining-digit check cover every scenario.

From understanding to recall

You have read the solution and it makes sense. Strip, loop, handle the tail, join. Four steps, no tricks. But can you write it from scratch without looking?

The details matter: using > 4 instead of > 3 as the loop condition, remembering to split 4 remaining digits into 2 + 2, and joining with dashes at the end. These are small decisions that are easy to second-guess under interview pressure.

Spaced repetition closes that gap. You practice writing the grouping loop from scratch at increasing intervals. After a few rounds, the pattern is automatic. You see "chunk a string into groups with a tail constraint" and the code flows out without hesitation.

The fixed-size chunking 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

CodeBricks breaks the reformat phone number problem into its character filtering and fixed-size chunking building blocks, then drills them independently with spaced repetition. You type each piece from scratch until the pattern is automatic. When a string grouping question shows up in your interview, you do not think about it. You just write it.