Facebook Pixel

423. Reconstruct Original Digits from English

MediumHash TableMathString
Leetcode Link

Problem Description

You are given a string s that contains jumbled letters representing the English words for digits 0 through 9. These letters are mixed up and out of order. Your task is to figure out which digits are represented and return them as a string in ascending numerical order.

For example, if the input string contains the letters that spell out "zero", "one", and "two" (but scrambled), you would need to identify these digits and return "012".

The key insight is that certain letters appear uniquely in specific digit words:

  • The letter 'z' only appears in "zero" (0)
  • The letter 'w' only appears in "two" (2)
  • The letter 'u' only appears in "four" (4)
  • The letter 'x' only appears in "six" (6)
  • The letter 'g' only appears in "eight" (8)

After counting these unique digits, you can deduce the remaining digits by subtracting the counts of already-identified digits from letters that appear in multiple digit words:

  • 'h' appears in "three" and "eight" → count of 3 = count of 'h' - count of 8
  • 'f' appears in "five" and "four" → count of 5 = count of 'f' - count of 4
  • 's' appears in "seven" and "six" → count of 7 = count of 's' - count of 6
  • 'o' appears in "zero", "one", "two", and "four" → count of 1 = count of 'o' - count of 0 - count of 2 - count of 4
  • 'i' appears in "five", "six", "eight", and "nine" → count of 9 = count of 'i' - count of 5 - count of 6 - count of 8

The solution counts the frequency of each letter in the input string, determines how many of each digit is present using the unique letter approach, and then constructs the output string by concatenating each digit the appropriate number of times in ascending order.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

The challenge here is that we have a jumbled string where multiple digit words overlap in their letters. For instance, both "one" and "zero" contain the letter 'o', making it difficult to directly count how many of each digit we have.

The breakthrough comes from realizing that some digits have unique identifying letters that don't appear in any other digit word. This gives us a starting point. If we map out all the digit words:

  • zero → z, e, r, o
  • one → o, n, e
  • two → t, w, o
  • three → t, h, r, e, e
  • four → f, o, u, r
  • five → f, i, v, e
  • six → s, i, x
  • seven → s, e, v, e, n
  • eight → e, i, g, h, t
  • nine → n, i, n, e

We can identify that certain letters are exclusive to specific digits. The letter 'z' only exists in "zero", 'w' only in "two", 'u' only in "four", 'x' only in "six", and 'g' only in "eight". This means we can directly count these digits first by counting their unique letters.

Once we've identified these "easy" digits (0, 2, 4, 6, 8), we can move to the next layer. Now we look for letters that appear in only two digit words, where one of those digits we've already counted. For example, 'h' appears only in "three" and "eight". Since we already know how many eights we have, we can calculate: count of three = total 'h' letters - count of eight.

This cascading approach continues. After determining digits 3, 5, and 7 using their semi-unique letters, we can finally calculate the remaining digits (1 and 9) by accounting for all the instances of their letters that we've already attributed to other digits.

The order of deduction is crucial - we must identify digits in a sequence that ensures we always have enough information to calculate the next set. This dependency chain is what makes the solution elegant and deterministic.

Solution Approach

The implementation follows a systematic counting and deduction approach using a frequency counter and an array to track digit counts.

First, we create a frequency counter for all characters in the input string using Counter(s). This gives us the total count of each letter present in the jumbled string.

We initialize an array cnt of size 10 to store the count of each digit from 0 to 9. The solution then proceeds in three phases:

Phase 1: Count digits with unique letters

cnt[0] = counter['z']  # 'z' only appears in "zero"
cnt[2] = counter['w']  # 'w' only appears in "two"
cnt[4] = counter['u']  # 'u' only appears in "four"
cnt[6] = counter['x']  # 'x' only appears in "six"
cnt[8] = counter['g']  # 'g' only appears in "eight"

Since these letters are unique to their respective digit words, we can directly assign their counts.

Phase 2: Deduce digits with semi-unique letters

cnt[3] = counter['h'] - cnt[8]  # 'h' appears in "three" and "eight"
cnt[5] = counter['f'] - cnt[4]  # 'f' appears in "five" and "four"
cnt[7] = counter['s'] - cnt[6]  # 's' appears in "seven" and "six"

For these digits, we subtract the count of already-identified digits from the total count of the semi-unique letter.

Phase 3: Calculate remaining digits

cnt[1] = counter['o'] - cnt[0] - cnt[2] - cnt[4]  # 'o' in "one", "zero", "two", "four"
cnt[9] = counter['i'] - cnt[5] - cnt[6] - cnt[8]  # 'i' in "nine", "five", "six", "eight"

For digits 1 and 9, we need to account for multiple previously counted digits that share their letters.

Finally, we construct the result string by concatenating each digit repeated by its count in ascending order:

return ''.join(cnt[i] * str(i) for i in range(10))

This generates a string where each digit i appears cnt[i] times, naturally maintaining ascending order since we iterate from 0 to 9.

The time complexity is O(n) where n is the length of the input string (for counting characters), and the space complexity is O(1) since we only use fixed-size data structures regardless of input size.

Ready to land your dream job?

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

Start Evaluator

Example Walkthrough

Let's walk through a small example with the input string s = "owoztneoer".

Step 1: Count character frequencies First, we count each character in the string:

  • o: 2
  • w: 1
  • z: 1
  • t: 1
  • n: 1
  • e: 2
  • r: 1

Step 2: Identify digits with unique letters We look for unique identifying letters:

  • 'z' appears 1 time → digit 0 appears 1 time (from "zero")
  • 'w' appears 1 time → digit 2 appears 1 time (from "two")
  • 'u' appears 0 times → digit 4 appears 0 times
  • 'x' appears 0 times → digit 6 appears 0 times
  • 'g' appears 0 times → digit 8 appears 0 times

Step 3: Deduce digits with semi-unique letters Now we calculate digits that share letters with already-identified digits:

  • digit 3: count of 'h' (0) - count of digit 8 (0) = 0
  • digit 5: count of 'f' (0) - count of digit 4 (0) = 0
  • digit 7: count of 's' (0) - count of digit 6 (0) = 0

Step 4: Calculate remaining digits Finally, we determine digits 1 and 9:

  • digit 1: count of 'o' (2) - count of digit 0 (1) - count of digit 2 (1) - count of digit 4 (0) = 2 - 1 - 1 - 0 = 0
    • Wait, let's recalculate: "zero" uses 1 'o', "two" uses 1 'o', so 2 - 1 - 1 = 0, but we have "one" which needs 'o', 'n', 'e'
    • Actually checking our letters: After removing "zero" (z,e,r,o) and "two" (t,w,o), we have letters: o,n,e,e,r remaining
    • This spells "one" (o,n,e) with e,r left over - so digit 1 appears 1 time

Let me recalculate more carefully:

  • After identifying "zero": removes z,e,r,o → remaining: o,w,t,n,e,o,r

  • After identifying "two": removes t,w,o → remaining: o,n,e,r

  • These remaining letters spell "one" perfectly!

  • digit 1 = 1

  • digit 9: count of 'i' (0) - count of digit 5 (0) - count of digit 6 (0) - count of digit 8 (0) = 0

Step 5: Build the result We concatenate each digit by its count in ascending order:

  • digit 0: 1 time → "0"
  • digit 1: 1 time → "1"
  • digit 2: 1 time → "2"
  • digits 3-9: 0 times → nothing

Result: "012"

The scrambled string "owoztneoer" contained the letters for "zero", "one", and "two", which we successfully identified and returned as "012" in ascending order.

Solution Implementation

1from collections import Counter
2from typing import List
3
4class Solution:
5    def originalDigits(self, s: str) -> str:
6        """
7        Reconstruct the original digits from a jumbled string of English words for digits.
8      
9        Strategy: Some digits have unique characters that appear only in their English words:
10        - 'z' only appears in "zero" (0)
11        - 'w' only appears in "two" (2)
12        - 'u' only appears in "four" (4)
13        - 'x' only appears in "six" (6)
14        - 'g' only appears in "eight" (8)
15      
16        After counting these unique digits, we can deduce the remaining digits:
17        - 'h' appears in "three" (3) and "eight" (8)
18        - 'f' appears in "five" (5) and "four" (4)
19        - 's' appears in "seven" (7) and "six" (6)
20        - 'o' appears in "one" (1), "zero" (0), "two" (2), and "four" (4)
21        - 'i' appears in "nine" (9), "five" (5), "six" (6), and "eight" (8)
22        """
23        # Count frequency of each character in the input string
24        char_counter = Counter(s)
25      
26        # Initialize array to store count of each digit (0-9)
27        digit_count = [0] * 10
28      
29        # First pass: Count digits with unique characters
30        digit_count[0] = char_counter['z']  # 'z' only in "zero"
31        digit_count[2] = char_counter['w']  # 'w' only in "two"
32        digit_count[4] = char_counter['u']  # 'u' only in "four"
33        digit_count[6] = char_counter['x']  # 'x' only in "six"
34        digit_count[8] = char_counter['g']  # 'g' only in "eight"
35      
36        # Second pass: Count digits that share characters with digits from first pass
37        digit_count[3] = char_counter['h'] - digit_count[8]  # 'h' in "three" and "eight"
38        digit_count[5] = char_counter['f'] - digit_count[4]  # 'f' in "five" and "four"
39        digit_count[7] = char_counter['s'] - digit_count[6]  # 's' in "seven" and "six"
40      
41        # Third pass: Count remaining digits
42        digit_count[1] = char_counter['o'] - digit_count[0] - digit_count[2] - digit_count[4]  # 'o' in "one", "zero", "two", "four"
43        digit_count[9] = char_counter['i'] - digit_count[5] - digit_count[6] - digit_count[8]  # 'i' in "nine", "five", "six", "eight"
44      
45        # Build result string with digits in ascending order
46        return ''.join(digit_count[i] * str(i) for i in range(10))
47
1class Solution {
2    public String originalDigits(String s) {
3        // Count frequency of each character in the input string
4        int[] charFrequency = new int[26];
5        for (char c : s.toCharArray()) {
6            charFrequency[c - 'a']++;
7        }
8      
9        // Array to store count of each digit (0-9)
10        int[] digitCount = new int[10];
11      
12        // First pass: Count digits with unique characters
13        // 'z' only appears in "zero"
14        digitCount[0] = charFrequency['z' - 'a'];
15        // 'w' only appears in "two"
16        digitCount[2] = charFrequency['w' - 'a'];
17        // 'u' only appears in "four"
18        digitCount[4] = charFrequency['u' - 'a'];
19        // 'x' only appears in "six"
20        digitCount[6] = charFrequency['x' - 'a'];
21        // 'g' only appears in "eight"
22        digitCount[8] = charFrequency['g' - 'a'];
23      
24        // Second pass: Count digits after removing counts from first pass
25        // 'h' appears in "three" and "eight"
26        digitCount[3] = charFrequency['h' - 'a'] - digitCount[8];
27        // 'f' appears in "five" and "four"
28        digitCount[5] = charFrequency['f' - 'a'] - digitCount[4];
29        // 's' appears in "seven" and "six"
30        digitCount[7] = charFrequency['s' - 'a'] - digitCount[6];
31      
32        // Third pass: Count remaining digits
33        // 'o' appears in "one", "zero", "two", and "four"
34        digitCount[1] = charFrequency['o' - 'a'] - digitCount[0] - digitCount[2] - digitCount[4];
35        // 'i' appears in "nine", "five", "six", and "eight"
36        digitCount[9] = charFrequency['i' - 'a'] - digitCount[5] - digitCount[6] - digitCount[8];
37      
38        // Build the result string with digits in ascending order
39        StringBuilder result = new StringBuilder();
40        for (int digit = 0; digit < 10; digit++) {
41            // Append each digit the number of times it appears
42            for (int count = 0; count < digitCount[digit]; count++) {
43                result.append(digit);
44            }
45        }
46      
47        return result.toString();
48    }
49}
50
1class Solution {
2public:
3    string originalDigits(string s) {
4        // Count frequency of each character in the input string
5        vector<int> charFrequency(26, 0);
6        for (char c : s) {
7            charFrequency[c - 'a']++;
8        }
9      
10        // Array to store count of each digit (0-9)
11        vector<int> digitCount(10, 0);
12      
13        // First pass: Count digits with unique characters
14        // 'z' only appears in "zero" (0)
15        digitCount[0] = charFrequency['z' - 'a'];
16        // 'w' only appears in "two" (2)
17        digitCount[2] = charFrequency['w' - 'a'];
18        // 'u' only appears in "four" (4)
19        digitCount[4] = charFrequency['u' - 'a'];
20        // 'x' only appears in "six" (6)
21        digitCount[6] = charFrequency['x' - 'a'];
22        // 'g' only appears in "eight" (8)
23        digitCount[8] = charFrequency['g' - 'a'];
24      
25        // Second pass: Count digits that can be determined after removing first pass digits
26        // 'h' appears in "three" (3) and "eight" (8)
27        digitCount[3] = charFrequency['h' - 'a'] - digitCount[8];
28        // 'f' appears in "five" (5) and "four" (4)
29        digitCount[5] = charFrequency['f' - 'a'] - digitCount[4];
30        // 's' appears in "seven" (7) and "six" (6)
31        digitCount[7] = charFrequency['s' - 'a'] - digitCount[6];
32      
33        // Third pass: Count remaining digits
34        // 'o' appears in "one" (1), "zero" (0), "two" (2), and "four" (4)
35        digitCount[1] = charFrequency['o' - 'a'] - digitCount[0] - digitCount[2] - digitCount[4];
36        // 'i' appears in "nine" (9), "five" (5), "six" (6), and "eight" (8)
37        digitCount[9] = charFrequency['i' - 'a'] - digitCount[5] - digitCount[6] - digitCount[8];
38      
39        // Build the result string with digits in ascending order
40        string result;
41        for (int digit = 0; digit < 10; digit++) {
42            for (int count = 0; count < digitCount[digit]; count++) {
43                result += char(digit + '0');
44            }
45        }
46      
47        return result;
48    }
49};
50
1function originalDigits(s: string): string {
2    // Count frequency of each character in the input string
3    const charFrequency: number[] = new Array(26).fill(0);
4    for (const c of s) {
5        charFrequency[c.charCodeAt(0) - 'a'.charCodeAt(0)]++;
6    }
7  
8    // Array to store count of each digit (0-9)
9    const digitCount: number[] = new Array(10).fill(0);
10  
11    // First pass: Count digits with unique characters
12    // 'z' only appears in "zero" (0)
13    digitCount[0] = charFrequency['z'.charCodeAt(0) - 'a'.charCodeAt(0)];
14    // 'w' only appears in "two" (2)
15    digitCount[2] = charFrequency['w'.charCodeAt(0) - 'a'.charCodeAt(0)];
16    // 'u' only appears in "four" (4)
17    digitCount[4] = charFrequency['u'.charCodeAt(0) - 'a'.charCodeAt(0)];
18    // 'x' only appears in "six" (6)
19    digitCount[6] = charFrequency['x'.charCodeAt(0) - 'a'.charCodeAt(0)];
20    // 'g' only appears in "eight" (8)
21    digitCount[8] = charFrequency['g'.charCodeAt(0) - 'a'.charCodeAt(0)];
22  
23    // Second pass: Count digits that can be determined after removing first pass digits
24    // 'h' appears in "three" (3) and "eight" (8)
25    digitCount[3] = charFrequency['h'.charCodeAt(0) - 'a'.charCodeAt(0)] - digitCount[8];
26    // 'f' appears in "five" (5) and "four" (4)
27    digitCount[5] = charFrequency['f'.charCodeAt(0) - 'a'.charCodeAt(0)] - digitCount[4];
28    // 's' appears in "seven" (7) and "six" (6)
29    digitCount[7] = charFrequency['s'.charCodeAt(0) - 'a'.charCodeAt(0)] - digitCount[6];
30  
31    // Third pass: Count remaining digits
32    // 'o' appears in "one" (1), "zero" (0), "two" (2), and "four" (4)
33    digitCount[1] = charFrequency['o'.charCodeAt(0) - 'a'.charCodeAt(0)] - digitCount[0] - digitCount[2] - digitCount[4];
34    // 'i' appears in "nine" (9), "five" (5), "six" (6), and "eight" (8)
35    digitCount[9] = charFrequency['i'.charCodeAt(0) - 'a'.charCodeAt(0)] - digitCount[5] - digitCount[6] - digitCount[8];
36  
37    // Build the result string with digits in ascending order
38    let result: string = '';
39    for (let digit = 0; digit < 10; digit++) {
40        for (let count = 0; count < digitCount[digit]; count++) {
41            result += String(digit);
42        }
43    }
44  
45    return result;
46}
47

Time and Space Complexity

Time Complexity: O(n)

  • Creating the Counter from string s takes O(n) time where n is the length of the input string
  • Initializing the cnt array with 10 elements takes O(1) time
  • All the counting operations (accessing Counter values and calculating cnt values) take O(1) time since we're performing a fixed number of operations regardless of input size
  • The final join operation iterates through 10 digits (0-9), and for each digit i, it creates cnt[i] repetitions of that digit. In the worst case, the total number of characters generated equals n (since we're reconstructing digits from the original string's characters), making this O(n)
  • Overall time complexity: O(n) + O(1) + O(n) = O(n)

Space Complexity: O(n)

  • The Counter object stores at most 26 unique characters (lowercase English letters) with their frequencies, which is O(1) in terms of unique keys, but the Counter itself requires O(n) space to process the string
  • The cnt array uses O(10) = O(1) space
  • The output string in the worst case contains all the digits reconstructed from the input, which could be proportional to the input size, requiring O(n) space
  • Overall space complexity: O(n)

Common Pitfalls

1. Incorrect Order of Deduction

Pitfall: Attempting to deduce digits in the wrong order can lead to incorrect counts. For example, trying to calculate the count of digit 3 before calculating the count of digit 8 would fail because we need to know how many 8s exist to properly deduce the 3s from the letter 'h'.

Why it happens: The letter 'h' appears in both "three" and "eight". If we try cnt[3] = counter['h'] without first determining cnt[8], we'll overcount the 3s.

Solution: Always follow the strict order:

  1. First identify digits with unique letters (0, 2, 4, 6, 8)
  2. Then deduce digits with semi-unique letters (3, 5, 7)
  3. Finally calculate the remaining digits (1, 9)

2. Forgetting to Account for All Overlapping Letters

Pitfall: When calculating digit 1, forgetting to subtract all digits that contain the letter 'o' will result in an incorrect count.

Incorrect approach:

# Wrong - only subtracting some digits
cnt[1] = counter['o'] - cnt[0] - cnt[2]  # Missing cnt[4]!

Correct approach:

# Right - subtracting all digits containing 'o'
cnt[1] = counter['o'] - cnt[0] - cnt[2] - cnt[4]

3. Using the Wrong Unique Letter Mapping

Pitfall: Misremembering which letters are unique to which digits. For instance, thinking 't' is unique to "two" when it actually appears in both "two" and "eight".

Solution: Create a reference table or comment clearly:

  • z → zero (0) ✓
  • w → two (2) ✓
  • u → four (4) ✓
  • x → six (6) ✓
  • g → eight (8) ✓
  • t → two, three, eight ✗ (not unique!)

4. Building the Result String Incorrectly

Pitfall: Concatenating digits in the wrong order or format:

Wrong approaches:

# Wrong - doesn't repeat digits based on their count
return ''.join(str(i) for i in range(10) if cnt[i] > 0)

# Wrong - doesn't maintain ascending order
result = ""
for i in range(10):
    if cnt[i] > 0:
        result = str(i) * cnt[i] + result  # Prepending causes reverse order!

Correct approach:

# Each digit i appears cnt[i] times in ascending order
return ''.join(cnt[i] * str(i) for i in range(10))

5. Not Handling Edge Cases

Pitfall: Not considering that some digits might not appear at all in the input string, leading to potential issues if not properly initialized.

Solution: Initialize the digit count array with zeros and ensure the Counter handles missing keys gracefully (which Python's Counter does by returning 0 for missing keys).

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

How many ways can you arrange the three letters A, B and C?


Recommended Readings

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

Load More