Skip to content
← All posts

Integer to English Words: Breaking Numbers into Groups

6 min read
leetcodeproblemhardstringsmath

LeetCode 273, Integer to English Words, asks you to convert a non-negative integer into its English words representation. The input can be anything from 0 up to 2^31 - 1 (about 2.1 billion). For example, 1234567 becomes "One Million Two Hundred Thirty Four Thousand Five Hundred Sixty Seven". The problem sounds like a lot of string manipulation, but the core idea is surprisingly clean once you see the pattern.

Problem description

Given a non-negative integer num, return its English words representation.

  • 0 returns "Zero"
  • 123 returns "One Hundred Twenty Three"
  • 12345 returns "Twelve Thousand Three Hundred Forty Five"
  • 1234567 returns "One Million Two Hundred Thirty Four Thousand Five Hundred Sixty Seven"
1,234,567 broken into groups of three1Million"One Million"234Thousand"Two Hundred Thirty Four Thousand"567Ones"Five Hundred Sixty Seven"Result: "One Million Two Hundred Thirty Four Thousand Five Hundred Sixty Seven"
Each group of up to three digits is converted independently, then a scale suffix (Thousand, Million, Billion) is appended.

The visual above shows the key insight. Every number can be split into groups of three digits, starting from the right. Each group gets converted to words independently, and then you attach a scale word (Thousand, Million, or Billion) based on the group's position.

The approach

The strategy is to repeatedly divide by 1000 and process each three-digit chunk. You work from right to left, peeling off the last three digits with num % 1000 and then shrinking the number with num // 1000. Each chunk gets its own conversion through a helper function, and you tack on the appropriate suffix.

The helper function itself handles three cases:

  1. If the number is below 20, look it up directly in a table. This covers "One" through "Nineteen".
  2. If the number is below 100, split it into a tens part and a ones part. "Forty" + "Five" = "Forty Five".
  3. If the number is 100 or above, extract the hundreds digit, convert it, add "Hundred", and recurse on the remainder.

That is the entire algorithm. Three cases in the helper, one loop in the main function.

The numbers 11 through 19 are the tricky part. They do not follow the pattern of "tens word + ones word" like the rest. "Eleven" is not "Ten One". By including all of them in a single lookup array alongside 1 through 10, you avoid any special branching. The array covers indices 0 through 19, and a single bounds check handles everything below 20.

The solution

def number_to_words(num):
    if num == 0:
        return "Zero"

    ones = ["", "One", "Two", "Three", "Four", "Five", "Six", "Seven",
            "Eight", "Nine", "Ten", "Eleven", "Twelve", "Thirteen",
            "Fourteen", "Fifteen", "Sixteen", "Seventeen", "Eighteen", "Nineteen"]
    tens = ["", "", "Twenty", "Thirty", "Forty", "Fifty",
            "Sixty", "Seventy", "Eighty", "Ninety"]
    thousands = ["", " Thousand", " Million", " Billion"]

    def helper(n):
        if n == 0:
            return ""
        if n < 20:
            return ones[n]
        if n < 100:
            rest = helper(n % 10)
            return tens[n // 10] + (" " + rest if rest else "")
        rest = helper(n % 100)
        return ones[n // 100] + " Hundred" + (" " + rest if rest else "")

    result = ""
    i = 0
    while num > 0:
        if num % 1000 != 0:
            part = helper(num % 1000)
            result = part + thousands[i] + (" " + result if result else "")
        num //= 1000
        i += 1
    return result

Here is what each part does:

  • Zero check. If the input is 0, return "Zero" immediately. The main loop would otherwise produce an empty string.
  • Lookup tables. The ones array covers empty string through "Nineteen". The tens array covers "Twenty" through "Ninety". The thousands array holds the scale suffixes.
  • Helper function. Converts any number from 0 to 999 into words using three recursive cases. It returns an empty string for 0, which lets the caller skip empty groups cleanly.
  • Main loop. Peels off three digits at a time with % 1000. If the chunk is not zero, convert it and prepend it to the result with the correct suffix. Then divide by 1000 and move to the next scale.

Step-by-step walkthrough

Let's trace through num = 1234567 to see how the groups get processed from right to left.

Step 1: Process rightmost 3 digits (num % 1000)

567"Five Hundred Sixty Seven"

567 % 1000 = 567. helper(567) returns "Five Hundred Sixty Seven". No suffix for the ones group. num becomes 1234.

Step 2: Process next 3 digits (num % 1000)

234"Two Hundred Thirty Four Thousand"

1234 % 1000 = 234. helper(234) returns "Two Hundred Thirty Four". Append " Thousand" suffix. num becomes 1.

Step 3: Process remaining digits (num % 1000)

1"One Million"

1 % 1000 = 1. helper(1) returns "One". Append " Million" suffix. num becomes 0, so we stop.

Done. num = 0.

Final: "One Million Two Hundred Thirty Four Thousand Five Hundred Sixty Seven"

The groups are combined from left to right, producing the full English representation.

Notice how each iteration processes exactly one group of up to three digits. The i counter tracks which suffix to attach. When a group is all zeros (like if the number were 1000567), the if num % 1000 != 0 check skips it entirely. No empty words end up in the result.

Complexity

ApproachTimeSpace
Group-of-three processingO(1)O(1)

Both time and space are constant because the input is bounded by 2^31 - 1. The main loop runs at most 4 iterations (ones, thousands, millions, billions). The helper function processes at most 3 digits per call. The lookup tables have fixed sizes. There is no variable-size input to scale with.

Building blocks

This problem is built from two reusable building blocks.

Chunk-and-suffix decomposition

The technique of breaking a number into fixed-size chunks from right to left using % k and // k. You process each chunk independently and attach a label based on its position. The template:

i = 0
while num > 0:
    chunk = num % k
    process(chunk, labels[i])
    num //= k
    i += 1

This same decomposition pattern appears in base conversion problems, where you repeatedly extract digits in a target base. It also shows up in problems that ask you to process numbers by place value.

Recursive case splitting

The helper function splits the problem into three non-overlapping ranges (below 20, below 100, below 1000) and handles each one differently. This is a general technique for problems where different ranges of input require different logic. By checking the smallest range first and recursing on the remainder, you keep the code clean and avoid nested conditionals.

Edge cases

Zero. The input 0 is a special case because the main loop never executes (the while condition num > 0 is immediately false). You handle this with an early return of "Zero".

Zeros in the middle. A number like 1000010 has a zero group in the thousands position. The number breaks into groups 1 (million), 000 (thousand), 010 (ones). The if num % 1000 != 0 check skips the empty thousands group, so you get "One Million Ten" with no extra spaces or dangling "Thousand".

Teens. Numbers like 15 must produce "Fifteen", not "Ten Five". The ones lookup table handles this by including all values from 0 through 19 in a single array. Any number below 20 gets a direct lookup with no splitting.

From understanding to recall

The group-of-three decomposition is the kind of pattern that makes total sense when you read it but is easy to fumble during an interview. The part most people forget is the teen handling. They remember the general idea of splitting into groups and adding suffixes, but then they write a helper that tries to handle 10-19 with tens[1] + ones[n % 10] and get "Ten Five" instead of "Fifteen".

The fix is simple: make the ones array go up to index 19 and check n < 20 before checking n < 100. That one design decision eliminates all the teen edge cases. If you drill writing the helper function a few times, the three-case structure (below 20, below 100, below 1000) becomes automatic.

Spaced repetition locks this in. You practice typing the full solution from scratch at increasing intervals. After a few cycles, the lookup tables and the recursive helper become second nature. When you encounter any number-to-string conversion problem, you already have the building blocks ready to assemble.

Related posts

  • Integer to Roman - Another number-to-string conversion problem that uses a lookup table and iterative decomposition
  • Roman to Integer - The reverse direction, parsing a string representation back into a number
  • Reverse Integer - Uses the same mod/divide loop to process digits one at a time