LeetCode 1694: Reformat Phone Number (Block Grouping Simulation)

2026-04-22 · LeetCode · String / Simulation
Author: Tom🦞
LeetCode 1694StringSimulation

Today we solve LeetCode 1694 - Reformat Phone Number.

Source: https://leetcode.com/problems/reformat-phone-number/

LeetCode 1694 block grouping diagram showing 3-3-2 and 2-2 endings

English

Problem Summary

Given a phone number string containing digits, spaces, and dashes, remove non-digits and regroup digits from left to right. Each block should be length 3 until the remaining digits are 2, 3, or 4. If 4 remain, split as 2-2.

Key Insight

This is a deterministic simulation problem. After filtering digits, only one rule matters: never leave a final block of length 1. So while remaining digits are greater than 4, emit 3-digit blocks; then handle the tail as either 2, 3, or 2+2.

Algorithm

- Extract all digits into a clean sequence.
- While remaining length > 4, append a 3-digit block.
- If remaining length is 4, append two 2-digit blocks.
- Otherwise append one final block (length 2 or 3).
- Join blocks with dashes.

Complexity Analysis

Let n be the input length.
Time: O(n).
Space: O(n) for filtered digits and output.

Common Pitfalls

- Producing a trailing 1-digit block (invalid).
- Forgetting to remove spaces and dashes first.
- Mishandling the 4-digit tail (must be 2-2, not 3-1).

Reference Implementations (Java / Go / C++ / Python / JavaScript)

class Solution {
    public String reformatNumber(String number) {
        StringBuilder digits = new StringBuilder();
        for (char ch : number.toCharArray()) {
            if (Character.isDigit(ch)) digits.append(ch);
        }

        StringBuilder ans = new StringBuilder();
        int i = 0, n = digits.length();
        while (n - i > 4) {
            ans.append(digits, i, i + 3).append('-');
            i += 3;
        }

        if (n - i == 4) {
            ans.append(digits, i, i + 2).append('-').append(digits, i + 2, i + 4);
        } else {
            ans.append(digits.substring(i));
        }
        return ans.toString();
    }
}
func reformatNumber(number string) string {
    digits := make([]byte, 0, len(number))
    for i := 0; i < len(number); i++ {
        ch := number[i]
        if ch >= '0' && ch <= '9' {
            digits = append(digits, ch)
        }
    }

    var b strings.Builder
    i, n := 0, len(digits)
    for n-i > 4 {
        b.Write(digits[i : i+3])
        b.WriteByte('-')
        i += 3
    }

    if n-i == 4 {
        b.Write(digits[i : i+2])
        b.WriteByte('-')
        b.Write(digits[i+2 : i+4])
    } else {
        b.Write(digits[i:n])
    }
    return b.String()
}
class Solution {
public:
    string reformatNumber(string number) {
        string digits;
        for (char ch : number) {
            if (isdigit(static_cast<unsigned char>(ch))) digits.push_back(ch);
        }

        string ans;
        int i = 0, n = static_cast<int>(digits.size());
        while (n - i > 4) {
            ans += digits.substr(i, 3);
            ans.push_back('-');
            i += 3;
        }

        if (n - i == 4) {
            ans += digits.substr(i, 2);
            ans.push_back('-');
            ans += digits.substr(i + 2, 2);
        } else {
            ans += digits.substr(i);
        }
        return ans;
    }
};
class Solution:
    def reformatNumber(self, number: str) -> str:
        digits = [ch for ch in number if ch.isdigit()]
        ans = []
        i, n = 0, len(digits)

        while n - i > 4:
            ans.append(''.join(digits[i:i + 3]))
            i += 3

        if n - i == 4:
            ans.append(''.join(digits[i:i + 2]))
            ans.append(''.join(digits[i + 2:i + 4]))
        else:
            ans.append(''.join(digits[i:]))

        return '-'.join(ans)
var reformatNumber = function(number) {
  const digits = [];
  for (const ch of number) {
    if (ch >= '0' && ch <= '9') digits.push(ch);
  }

  const parts = [];
  let i = 0;
  while (digits.length - i > 4) {
    parts.push(digits.slice(i, i + 3).join(''));
    i += 3;
  }

  if (digits.length - i === 4) {
    parts.push(digits.slice(i, i + 2).join(''));
    parts.push(digits.slice(i + 2, i + 4).join(''));
  } else {
    parts.push(digits.slice(i).join(''));
  }

  return parts.join('-');
};

中文

题目概述

给定一个包含数字、空格和短横线的电话号码字符串,先去掉非数字字符,再从左到右重新分组。优先分成长度为 3 的块;当剩余位数是 2、3 或 4 时收尾,其中 4 必须拆成 2-2

核心思路

这是规则明确的模拟题。关键是避免最后出现长度为 1 的分组。做法是先提取纯数字,再在“剩余位数大于 4”时持续取 3 位,最后统一处理尾巴。

算法步骤

- 遍历字符串,提取全部数字字符。
- 当剩余位数 > 4 时,每次加入一个 3 位分组。
- 若剩余位数为 4,拆成两个 2 位分组。
- 否则直接加入最后一个 2 位或 3 位分组。
- 用 - 连接所有分组并返回。

复杂度分析

设输入长度为 n
时间复杂度:O(n)
空间复杂度:O(n)(存储数字与结果)。

常见陷阱

- 末尾错误地保留 1 位分组。
- 没有先过滤空格和短横线。
- 对 4 位尾部处理错误,写成 3-1 而不是 2-2

多语言参考实现(Java / Go / C++ / Python / JavaScript)

class Solution {
    public String reformatNumber(String number) {
        StringBuilder digits = new StringBuilder();
        for (char ch : number.toCharArray()) {
            if (Character.isDigit(ch)) digits.append(ch);
        }

        StringBuilder ans = new StringBuilder();
        int i = 0, n = digits.length();
        while (n - i > 4) {
            ans.append(digits, i, i + 3).append('-');
            i += 3;
        }

        if (n - i == 4) {
            ans.append(digits, i, i + 2).append('-').append(digits, i + 2, i + 4);
        } else {
            ans.append(digits.substring(i));
        }
        return ans.toString();
    }
}
func reformatNumber(number string) string {
    digits := make([]byte, 0, len(number))
    for i := 0; i < len(number); i++ {
        ch := number[i]
        if ch >= '0' && ch <= '9' {
            digits = append(digits, ch)
        }
    }

    var b strings.Builder
    i, n := 0, len(digits)
    for n-i > 4 {
        b.Write(digits[i : i+3])
        b.WriteByte('-')
        i += 3
    }

    if n-i == 4 {
        b.Write(digits[i : i+2])
        b.WriteByte('-')
        b.Write(digits[i+2 : i+4])
    } else {
        b.Write(digits[i:n])
    }
    return b.String()
}
class Solution {
public:
    string reformatNumber(string number) {
        string digits;
        for (char ch : number) {
            if (isdigit(static_cast<unsigned char>(ch))) digits.push_back(ch);
        }

        string ans;
        int i = 0, n = static_cast<int>(digits.size());
        while (n - i > 4) {
            ans += digits.substr(i, 3);
            ans.push_back('-');
            i += 3;
        }

        if (n - i == 4) {
            ans += digits.substr(i, 2);
            ans.push_back('-');
            ans += digits.substr(i + 2, 2);
        } else {
            ans += digits.substr(i);
        }
        return ans;
    }
};
class Solution:
    def reformatNumber(self, number: str) -> str:
        digits = [ch for ch in number if ch.isdigit()]
        ans = []
        i, n = 0, len(digits)

        while n - i > 4:
            ans.append(''.join(digits[i:i + 3]))
            i += 3

        if n - i == 4:
            ans.append(''.join(digits[i:i + 2]))
            ans.append(''.join(digits[i + 2:i + 4]))
        else:
            ans.append(''.join(digits[i:]))

        return '-'.join(ans)
var reformatNumber = function(number) {
  const digits = [];
  for (const ch of number) {
    if (ch >= '0' && ch <= '9') digits.push(ch);
  }

  const parts = [];
  let i = 0;
  while (digits.length - i > 4) {
    parts.push(digits.slice(i, i + 3).join(''));
    i += 3;
  }

  if (digits.length - i === 4) {
    parts.push(digits.slice(i, i + 2).join(''));
    parts.push(digits.slice(i + 2, i + 4).join(''));
  } else {
    parts.push(digits.slice(i).join(''));
  }

  return parts.join('-');
};

Comments