Reconstruct Original Digits from English
MedPrompt
Given a string s containing an out-of-order English representation of digits 0-9, return the digits in ascending order.
Example 1:
Input: s = "owoztneoer"
Output: "012"Example 2:
Input: s = "fviefuro"
Output: "45"
Constraints:
1 <= s.length <= 105s[i]is one of the characters["e","g","f","i","h","o","n","s","r","u","t","w","v","x","z"].sis guaranteed to be valid.
Approaches
2 approaches with complexity analysis and trade-offs.
This approach frames the problem as finding the correct count for each digit (0-9) that accounts for all characters in the input string. It uses a recursive backtracking algorithm to explore all possible combinations of digit counts.
Algorithm
- Create a frequency map
charCountsfor the characters ins. - Create an array
digitCountsof size 10 to store the result. - Define a recursive function
backtrack(digit, currentCounts):- Base Case: If
digit == 10, check if all values incurrentCountsare 0. If yes, a solution is found, returntrue; otherwise, returnfalse. - Determine the word for the current
digit. - Calculate the maximum possible number of times (
maxCount) this word can be formed fromcurrentCounts. - Loop
ifrom 0 tomaxCount:- Set
digitCounts[digit] = i. - Create
nextCountsby subtracting characters foricopies of the word fromcurrentCounts. - If
backtrack(digit + 1, nextCounts)returnstrue, then a solution is found down this path, so returntrue.
- Set
- If the loop completes, no solution was found for this branch. Return
false.
- Base Case: If
- Initiate the process with
backtrack(0, charCounts). - If it returns
true, build the result string fromdigitCountsby appending each digit the required number of times in ascending order.
Walkthrough
First, we count the frequency of each character in the input string s. We then define a recursive function, say backtrack(digit, counts), which tries to determine the number of occurrences for the current digit. The function iterates through all possible counts for the current digit, from 0 up to the maximum possible (limited by the available characters in counts). For each possible count, it subtracts the corresponding characters from the counts map and makes a recursive call for the next digit (digit + 1). If a recursive call returns true (meaning a valid combination was found for the subsequent digits), it means we've found a solution. We store the count for the current digit and also return true. The base case for the recursion is when we have considered all digits (i.e., digit == 10). At this point, if all character counts are zero, we have successfully reconstructed the original digits. Once the backtracking process successfully finds the counts for all digits, we construct the final string.
class Solution { // Word representations for digits 0-9 String[] words = {"zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"}; // Character counts for each word int[][] wordCharCounts = new int[10][26]; // Resulting digit counts int[] digitCounts = new int[10]; public String originalDigits(String s) { // Pre-calculate character counts for each digit word for (int i = 0; i < 10; i++) { for (char c : words[i].toCharArray()) { wordCharCounts[i][c - 'a']++; } } // Count characters in the input string int[] sCounts = new int[26]; for (char c : s.toCharArray()) { sCounts[c - 'a']++; } // Start backtracking from digit 0 if (backtrack(0, sCounts)) { StringBuilder result = new StringBuilder(); for (int i = 0; i < 10; i++) { for (int j = 0; j < digitCounts[i]; j++) { result.append(i); } } return result.toString(); } return ""; // Should not happen based on problem constraints } private boolean backtrack(int digit, int[] currentCounts) { if (digit == 10) { // Base case: check if all characters have been used for (int count : currentCounts) { if (count != 0) { return false; } } return true; } // Determine max possible count for the current digit's word int maxCount = Integer.MAX_VALUE; for (int i = 0; i < 26; i++) { if (wordCharCounts[digit][i] > 0) { maxCount = Math.min(maxCount, currentCounts[i] / wordCharCounts[digit][i]); } } // Iterate through all possible counts for the current digit for (int i = 0; i <= maxCount; i++) { int[] nextCounts = new int[26]; System.arraycopy(currentCounts, 0, nextCounts, 0, 26); for (int j = 0; j < 26; j++) { nextCounts[j] -= i * wordCharCounts[digit][j]; } digitCounts[digit] = i; if (backtrack(digit + 1, nextCounts)) { return true; } } return false; }}Complexity
Time
O(K^10 * L) where K is the maximum possible count for any single digit, and L is the alphabet size (26). The recursion depth is 10, and at each level, we can branch up to K times. This is exponential and too slow for the given constraints.
Space
O(1). The recursion depth is constant (10), and at each level, we store a character count array of constant size (26).
Trade-offs
Pros
It's a general approach that could work for similar problems where a direct counting trick isn't obvious.
Cons
Extremely inefficient due to its exponential time complexity.
Will result in a 'Time Limit Exceeded' (TLE) error for the given constraints.
More complex to implement correctly compared to the optimal solution.
This approach leverages the fact that some digits have unique characters in their English spelling. By identifying these unique characters, we can determine the counts of certain digits directly. We can then use these counts to deduce the counts of other digits in a specific, carefully chosen order.
Algorithm
- Create a frequency map
charCounts(an array of size 26) for the characters in the input strings. - Create an array
digitCountsof size 10, initialized to zero, to store the final counts of each digit. - Calculate digit counts based on unique characters in a specific order:
digitCounts[0] = charCounts['z' - 'a']digitCounts[2] = charCounts['w' - 'a']digitCounts[4] = charCounts['u' - 'a']digitCounts[6] = charCounts['x' - 'a']digitCounts[8] = charCounts['g' - 'a']
- Calculate counts for digits that can now be uniquely identified:
digitCounts[3] = charCounts['h' - 'a'] - digitCounts[8]digitCounts[5] = charCounts['f' - 'a'] - digitCounts[4]digitCounts[7] = charCounts['s' - 'a'] - digitCounts[6]
- Calculate the remaining digit counts:
digitCounts[1] = charCounts['o' - 'a'] - digitCounts[0] - digitCounts[2] - digitCounts[4]digitCounts[9] = charCounts['i' - 'a'] - digitCounts[5] - digitCounts[6] - digitCounts[8]
- Create a
StringBuilder. - Iterate from
i = 0to9. For eachi, append the character(char)('0' + i)to theStringBuilderdigitCounts[i]times. - Return the
StringBuilder's string representation.
Walkthrough
The key insight is to find an order of identification for the digits 0-9. We first count the frequency of all characters in the input string s. We observe that some characters uniquely identify a digit:
- 'z' appears only in "zero".
- 'w' appears only in "two".
- 'u' appears only in "four".
- 'x' appears only in "six".
- 'g' appears only in "eight". This allows us to immediately determine the counts of digits 0, 2, 4, 6, and 8. After accounting for the characters used by these digits, we can find characters that are now unique for the remaining digits.
- 'h' appears in "three" and "eight". Since we know the count of "eight", we can find the count of "three".
- 'f' appears in "five" and "four". We can find the count of "five".
- 's' appears in "seven" and "six". We can find the count of "seven". Finally, we can determine the counts of the last remaining digits, 'one' and 'nine', using characters like 'o' or 'i' and subtracting the counts from digits we've already solved. Once we have the counts of all ten digits, we build the result string by appending each digit the required number of times in ascending order.
class Solution { public String originalDigits(String s) { // Step 1: Count character frequencies int[] charCounts = new int[26]; for (char c : s.toCharArray()) { charCounts[c - 'a']++; } // Step 2: Create an array to store digit counts int[] digitCounts = new int[10]; // Step 3: Calculate counts based on unique characters // Unique characters: z, w, u, x, g digitCounts[0] = charCounts['z' - 'a']; // "zero" digitCounts[2] = charCounts['w' - 'a']; // "two" digitCounts[4] = charCounts['u' - 'a']; // "four" digitCounts[6] = charCounts['x' - 'a']; // "six" digitCounts[8] = charCounts['g' - 'a']; // "eight" // Step 4: Calculate counts for digits that can now be identified // 'h' in "three" and "eight" digitCounts[3] = charCounts['h' - 'a'] - digitCounts[8]; // 'f' in "five" and "four" digitCounts[5] = charCounts['f' - 'a'] - digitCounts[4]; // 's' in "seven" and "six" digitCounts[7] = charCounts['s' - 'a'] - digitCounts[6]; // Step 5: Calculate remaining digit counts // 'i' in "nine", "five", "six", "eight" digitCounts[9] = charCounts['i' - 'a'] - digitCounts[5] - digitCounts[6] - digitCounts[8]; // 'o' in "one", "zero", "two", "four" digitCounts[1] = charCounts['o' - 'a'] - digitCounts[0] - digitCounts[2] - digitCounts[4]; // Step 6: Build the result string StringBuilder result = new StringBuilder(); for (int i = 0; i <= 9; i++) { for (int j = 0; j < digitCounts[i]; j++) { result.append(i); } } return result.toString(); }}Complexity
Time
O(N), where N is the length of the input string `s`. The initial character counting takes O(N). The subsequent calculations of digit counts take constant time, O(1). Building the final string takes time proportional to the number of digits, which is at most N. Therefore, the overall complexity is linear.
Space
O(1). We use a constant amount of extra space for the character counts array (size 26) and the digit counts array (size 10). The space for the result string is not counted as auxiliary space.
Trade-offs
Pros
Highly efficient with linear time complexity.
Simple to implement and understand.
Guaranteed to find the correct solution due to the unique properties of the English spellings of digits.
Cons
This approach is very specific to this particular problem and the English language. It's not a general-purpose algorithm.
Solutions
Solution
class Solution { public String originalDigits ( String s ) { int [] counter = new int [ 26 ]; for ( char c : s . toCharArray ()) { ++ counter [ c - 'a' ]; } int [] cnt = new int [ 10 ]; cnt [ 0 ] = counter [ 'z' - 'a' ]; cnt [ 2 ] = counter [ 'w' - 'a' ]; cnt [ 4 ] = counter [ 'u' - 'a' ]; cnt [ 6 ] = counter [ 'x' - 'a' ]; cnt [ 8 ] = counter [ 'g' - 'a' ]; cnt [ 3 ] = counter [ 'h' - 'a' ] - cnt [ 8 ]; cnt [ 5 ] = counter [ 'f' - 'a' ] - cnt [ 4 ]; cnt [ 7 ] = counter [ 's' - 'a' ] - cnt [ 6 ]; cnt [ 1 ] = counter [ 'o' - 'a' ] - cnt [ 0 ] - cnt [ 2 ] - cnt [ 4 ]; cnt [ 9 ] = counter [ 'i' - 'a' ] - cnt [ 5 ] - cnt [ 6 ] - cnt [ 8 ]; StringBuilder sb = new StringBuilder (); for ( int i = 0 ; i < 10 ; ++ i ) { for ( int j = 0 ; j < cnt [ i ]; ++ j ) { sb . append ( i ); } } return sb . toString (); } }class Solution { public: string originalDigits ( string s ) { vector < int > counter ( 26 ); for ( char c : s ) ++ counter [ c - 'a' ]; vector < int > cnt ( 10 ); cnt [ 0 ] = counter [ 'z' - 'a' ]; cnt [ 2 ] = counter [ 'w' - 'a' ]; cnt [ 4 ] = counter [ 'u' - 'a' ]; cnt [ 6 ] = counter [ 'x' - 'a' ]; cnt [ 8 ] = counter [ 'g' - 'a' ]; cnt [ 3 ] = counter [ 'h' - 'a' ] - cnt [ 8 ]; cnt [ 5 ] = counter [ 'f' - 'a' ] - cnt [ 4 ]; cnt [ 7 ] = counter [ 's' - 'a' ] - cnt [ 6 ]; cnt [ 1 ] = counter [ 'o' - 'a' ] - cnt [ 0 ] - cnt [ 2 ] - cnt [ 4 ]; cnt [ 9 ] = counter [ 'i' - 'a' ] - cnt [ 5 ] - cnt [ 6 ] - cnt [ 8 ]; string ans ; for ( int i = 0 ; i < 10 ; ++ i ) for ( int j = 0 ; j < cnt [ i ]; ++ j ) ans += char ( i + '0' ); return ans ; } };class Solution : def originalDigits ( self , s : str ) -> str : counter = Counter ( s ) cnt = [ 0 ] * 10 cnt [ 0 ] = counter [ 'z' ] cnt [ 2 ] = counter [ 'w' ] cnt [ 4 ] = counter [ 'u' ] cnt [ 6 ] = counter [ 'x' ] cnt [ 8 ] = counter [ 'g' ] cnt [ 3 ] = counter [ 'h' ] - cnt [ 8 ] cnt [ 5 ] = counter [ 'f' ] - cnt [ 4 ] cnt [ 7 ] = counter [ 's' ] - cnt [ 6 ] cnt [ 1 ] = counter [ 'o' ] - cnt [ 0 ] - cnt [ 2 ] - cnt [ 4 ] cnt [ 9 ] = counter [ 'i' ] - cnt [ 5 ] - cnt [ 6 ] - cnt [ 8 ] return '' . join ( cnt [ i ] * str ( i ) for i in range ( 10 ))Video walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.