Skip to content

Commit 59a9734

Browse files
My solution for 273
1 parent c499471 commit 59a9734

File tree

1 file changed

+286
-3
lines changed

1 file changed

+286
-3
lines changed

problems/273/jeremymanning.md

Lines changed: 286 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,294 @@
11
# [Problem 273: Integer to English Words](https://leetcode.com/problems/integer-to-english-words/description/?envType=daily-question)
22

33
## Initial thoughts (stream-of-consciousness)
4+
- I think this problem will need to be solved partly by hard-coding some number-to-word mappings, and partly by applying patterns
5+
- We'll need to start parsing by converting the number (integer) to a string
6+
- There's also a recursive component-- e.g., 12345 needs to be split into:
7+
- 12 (parsed as "Twelve", recursively using the "hundreds" parsing code) + " Thousand"
8+
- 345 (parsed as "Three Hundred Forty Five")
9+
- First, let's write a function for parsing 0--9 (single digits)
10+
- Then we can write a function for parsing 10--99 (double digits)
11+
- 10--19 need to be coded as special cases
12+
- After that, we just need to hard code in Twenty, Thirty, ..., Ninety + a call to the function for parsing single digits
13+
- To parse hundreds, we simply:
14+
- Parse the single (leading) number ("" if 0) and add " Hundred"
15+
- Then add " " + parse of the remaining two digits
16+
- To parse thousands, we can parse hundreds and add " Thousand", and similarly for parsing millions and billions
17+
- We don't need to go beyond billions, since `0 <= num <= 2^31 - 1`
18+
- To do this efficiently, let's write a base function that includes an argument for the prefix
19+
- Then we can just call the functions in blocks of 3 digits:
20+
- First the last 3: parse using "hundreds"
21+
- Next, the second-to-last 3: parse using "thousands" and prepend to the result
22+
- Next, the third-to-last 3: parse using "millions" and prepend to the result
23+
- Finally, the fourth-to-last "3": parse using "billions" and prepend to the result
24+
- The main "trickiness" in this problem will come from making sure we cover all of the edge cases. We'll need to test carefully.
425

526
## Refining the problem, round 2 thoughts
6-
27+
- We'll need the following functions (note: here I'm being sloppy with notation by not being explicit about converting to ints or strs, but in the actual implementation we'll need to handle type casting correctly):
28+
- `ParseSingle(x)`: maps single digits 0--9 onto words (hard code)
29+
- `ParseDouble(x)`: maps double digits 10--99 onto words:
30+
- Hard code 10--19
31+
- Hard code multiples of 10 between 20 and 90, inclusive
32+
- If `x > 19` and `x % 10 != 0` then return `ParseDouble(10 * x // 10) + " " + ParseSingle(x[1])`
33+
- `ParseTriple(x)`: maps triple digits onto words:
34+
- This is straightforward:
35+
- If `len(x) == 1` return `ParseSingle(x)`
36+
- Else if `len(x) == 2` return `ParseDouble(x)`
37+
- Else if `x % 100 == 0` return `ParseSingle(x[0]) + " Hundred"`
38+
- Otherwise return `ParseSingle(x[0] + " " + suffix + " Hundred " + ParseDouble([x[1:]])
39+
- `ParseThousands(x)`:
40+
- return `ParseTriple(x) + " Thousand"`
41+
- `ParseMillions(x)`:
42+
- return `ParseTriple(x) + " Million"`
43+
- `ParseBillions(x)`:
44+
- return `ParseTriple(x) + " Billion"`
45+
- `Parse(x)`:
46+
- Break `x` into chunks of 3 (starting from the end)-- we could just hard code in the cases, since there aren't many of them
47+
- If `1 <= len(x) <= 3`:
48+
- return `ParseTriple(x)`
49+
- Elif `4 <= len(x) <= 6`:
50+
- return `ParseThousands(x[-6:-3]) + " " + ParseTriple(x[-3:])`
51+
- Elif `7 <= len(x) <= 9`:
52+
- return `ParseMillions(x[-9:-6]) + " " + ParseThousands(x[-6:-3]) + " " + ParseTriple(x[-3:])`
53+
- Elif `10 <= len(x) <= 12`:
54+
- return `ParseBillions(x[-12:-9]) + " " + ParseMillions(x[-9:-6]) + " " + ParseThousands(x[-6:-3]) + " " + ParseTriple(x[-3:])`
55+
- Note: actually, I think we can do this more cleanly by separately parsing the billions, millions, thousands, and hundreds, and then joining everything together. This will also make it easier to handle spacing.
56+
- Let's go with this. Again, though, we're going to need to test everything carefully using a bunch of example cases to be sure we've accounted for everything needed.
757
## Attempted solution(s)
858
```python
9-
class Solution: # paste your code here!
10-
...
59+
class Solution:
60+
def numberToWords(self, num: int) -> str:
61+
if num == 0:
62+
return "Zero"
63+
64+
def ParseSingle(x):
65+
map = {'0': 'Zero', '1': 'One', '2': 'Two', '3': 'Three', '4': 'Four', '5': 'Five',
66+
'6': 'Six', '7': 'Seven', '8': 'Eight', '9': 'Nine'}
67+
return map[x]
68+
69+
def ParseDouble(x):
70+
map = {'10': 'Ten', '11': 'Eleven', '12': 'Twelve', '13': 'Thirteen', '14': 'Fourteen', '15': 'Fifteen',
71+
'16': 'Sixteen', '17': 'Seventeen', '18': 'Eighteen', '19': 'Nineteen',
72+
'20': 'Twenty', '30': 'Thirty', '40': 'Forty', '50': 'Fifty',
73+
'60': 'Sixty', '70': 'Seventy', '80': 'Eighty', '90': 'Ninety'}
74+
if x in map:
75+
return map[x]
76+
else:
77+
return map[str(10 * (int(x) // 10))] + " " + ParseSingle(x[1])
78+
79+
def ParseTriple(x):
80+
if len(x) == 1:
81+
return ParseSingle(x)
82+
elif len(x) == 2:
83+
return ParseDouble(x)
84+
elif int(x[0]) == 0:
85+
return ParseDouble(x[1:])
86+
elif int(x[1:]) == 0:
87+
return ParseSingle(x[0]) + " Hundred"
88+
else:
89+
return ParseSingle(x[0]) + " Hundred " + ParseDouble(x[1:])
90+
91+
def ParseThousands(x):
92+
if int(x) == 0:
93+
return ""
94+
return ParseTriple(x) + " Thousand"
95+
96+
def ParseMillions(x):
97+
if int(x) == 0:
98+
return ""
99+
return ParseTriple(x) + " Million"
100+
101+
def ParseBillions(x):
102+
if int(x) == 0:
103+
return ""
104+
return ParseTriple(x) + " Billion"
105+
106+
x = str(num)
107+
n = len(x)
108+
109+
# Breaking into groups of 3 digits
110+
billion = x[-12:-9] if n > 9 else ""
111+
million = x[-9:-6] if n > 6 else ""
112+
thousand = x[-6:-3] if n > 3 else ""
113+
hundred = x[-3:]
114+
115+
result = []
116+
if billion:
117+
result.append(ParseBillions(billion))
118+
if million:
119+
result.append(ParseMillions(million))
120+
if thousand:
121+
result.append(ParseThousands(thousand))
122+
if hundred:
123+
result.append(ParseTriple(hundred))
124+
125+
return ' '.join([x for x in result if len(x) > 0])
126+
```
127+
- Given test cases pass
128+
- Let's try a bunch of other cases:
129+
- 0: pass
130+
- 10: pass
131+
- 2918473: pass
132+
- 1478349587: pass
133+
- 49: pass
134+
- Ok...let's submit this!
135+
136+
![Screenshot 2024-08-06 at 11 00 48 PM](https://github.com/user-attachments/assets/51886cce-4ee9-4c49-b108-5e08bcc0478d)
137+
138+
Uh oh...looks like I've missed something 😞. Womp womp 🎺. Let's see what's going on...
139+
- I think the issue is with `ParseDouble`: I forgot to handle cases where (if we're calling `ParseDouble` from `ParseTriple`) the result of `int(x) // 10` could be 0. I'm just going to hard code in that case by mapping `"0"` to `""` inside of `ParseDouble`.
140+
- However, when I do that, the spacing gets messed up. Rather than fixing it in a clean way (🙃) I'll just "hack" the solution at the end by splitting/joining the final result.
141+
- Revised solution:
142+
143+
```python
144+
class Solution:
145+
def numberToWords(self, num: int) -> str:
146+
def ParseSingle(x):
147+
map = {'0': 'Zero', '1': 'One', '2': 'Two', '3': 'Three', '4': 'Four', '5': 'Five',
148+
'6': 'Six', '7': 'Seven', '8': 'Eight', '9': 'Nine'}
149+
return map[x]
150+
151+
def ParseDouble(x):
152+
map = {'10': 'Ten', '11': 'Eleven', '12': 'Twelve', '13': 'Thirteen', '14': 'Fourteen', '15': 'Fifteen',
153+
'16': 'Sixteen', '17': 'Seventeen', '18': 'Eighteen', '19': 'Nineteen',
154+
'20': 'Twenty', '30': 'Thirty', '40': 'Forty', '50': 'Fifty',
155+
'60': 'Sixty', '70': 'Seventy', '80': 'Eighty', '90': 'Ninety', "0": ""}
156+
if x in map:
157+
return map[x]
158+
else:
159+
return map[str(10 * (int(x) // 10))] + " " + ParseSingle(x[1])
160+
161+
def ParseTriple(x):
162+
if len(x) == 1:
163+
return ParseSingle(x)
164+
elif len(x) == 2:
165+
return ParseDouble(x)
166+
elif int(x[0]) == 0:
167+
return ParseDouble(x[1:])
168+
elif int(x[1:]) == 0:
169+
return ParseSingle(x[0]) + " Hundred"
170+
else:
171+
return ParseSingle(x[0]) + " Hundred " + ParseDouble(x[1:])
172+
173+
def ParseThousands(x):
174+
if int(x) == 0:
175+
return ""
176+
return ParseTriple(x) + " Thousand"
177+
178+
def ParseMillions(x):
179+
if int(x) == 0:
180+
return ""
181+
return ParseTriple(x) + " Million"
182+
183+
def ParseBillions(x):
184+
if int(x) == 0:
185+
return ""
186+
return ParseTriple(x) + " Billion"
187+
188+
x = str(num)
189+
n = len(x)
190+
191+
# Breaking into groups of 3 digits
192+
billion = x[-12:-9] if n > 9 else ""
193+
million = x[-9:-6] if n > 6 else ""
194+
thousand = x[-6:-3] if n > 3 else ""
195+
hundred = x[-3:]
196+
197+
result = []
198+
if billion:
199+
result.append(ParseBillions(billion))
200+
if million:
201+
result.append(ParseMillions(million))
202+
if thousand:
203+
result.append(ParseThousands(thousand))
204+
if hundred:
205+
result.append(ParseTriple(hundred))
206+
207+
result = ' '.join([x for x in result if len(x) > 0])
208+
return ' '.join(result.split()) # clean up white spaces
11209
```
210+
- Submitting without checking anything!! This is life in the fast lane... ⚠️ 🚗 ⚠️
211+
212+
![Screenshot 2024-08-06 at 11 14 45 PM](https://github.com/user-attachments/assets/f446ebc6-3c5c-4dbc-aad0-dfaa41615556)
213+
214+
Uh oh. What have I done here... "One Thousand Zero" definitely isn't a thing...🤦 Gah!!
215+
216+
Ok, let's try to be careful here...
217+
218+
## More notes...
219+
- I think the problem is because, in `ParseSingle`, "0" should actually *not* map to "Zero" if that function is called from the `ParseDouble` function. I think I can just add a flag to handle this.
220+
221+
```python
222+
class Solution:
223+
def numberToWords(self, num: int) -> str:
224+
def ParseSingle(x, ignore_zero=False):
225+
map = {'0': 'Zero', '1': 'One', '2': 'Two', '3': 'Three', '4': 'Four', '5': 'Five',
226+
'6': 'Six', '7': 'Seven', '8': 'Eight', '9': 'Nine'}
227+
if x == "0" and ignore_zero:
228+
return ""
229+
return map[x]
230+
231+
def ParseDouble(x):
232+
map = {'10': 'Ten', '11': 'Eleven', '12': 'Twelve', '13': 'Thirteen', '14': 'Fourteen', '15': 'Fifteen',
233+
'16': 'Sixteen', '17': 'Seventeen', '18': 'Eighteen', '19': 'Nineteen',
234+
'20': 'Twenty', '30': 'Thirty', '40': 'Forty', '50': 'Fifty',
235+
'60': 'Sixty', '70': 'Seventy', '80': 'Eighty', '90': 'Ninety', "0": ""}
236+
if x in map:
237+
return map[x]
238+
else:
239+
return map[str(10 * (int(x) // 10))] + " " + ParseSingle(x[1], ignore_zero=True)
240+
241+
def ParseTriple(x):
242+
if len(x) == 1:
243+
return ParseSingle(x)
244+
elif len(x) == 2:
245+
return ParseDouble(x)
246+
elif int(x[0]) == 0:
247+
return ParseDouble(x[1:])
248+
elif int(x[1:]) == 0:
249+
return ParseSingle(x[0]) + " Hundred"
250+
else:
251+
return ParseSingle(x[0]) + " Hundred " + ParseDouble(x[1:])
252+
253+
def ParseThousands(x):
254+
if int(x) == 0:
255+
return ""
256+
return ParseTriple(x) + " Thousand"
257+
258+
def ParseMillions(x):
259+
if int(x) == 0:
260+
return ""
261+
return ParseTriple(x) + " Million"
262+
263+
def ParseBillions(x):
264+
if int(x) == 0:
265+
return ""
266+
return ParseTriple(x) + " Billion"
267+
268+
x = str(num)
269+
n = len(x)
270+
271+
# Breaking into groups of 3 digits
272+
billion = x[-12:-9] if n > 9 else ""
273+
million = x[-9:-6] if n > 6 else ""
274+
thousand = x[-6:-3] if n > 3 else ""
275+
hundred = x[-3:]
276+
277+
result = []
278+
if billion:
279+
result.append(ParseBillions(billion))
280+
if million:
281+
result.append(ParseMillions(million))
282+
if thousand:
283+
result.append(ParseThousands(thousand))
284+
if hundred:
285+
result.append(ParseTriple(hundred))
286+
287+
result = ' '.join([x for x in result if len(x) > 0])
288+
return ' '.join(result.split()) # clean up white spaces
289+
```
290+
- Ok...submitting again 🤞...
291+
292+
![Screenshot 2024-08-06 at 11 29 07 PM](https://github.com/user-attachments/assets/9ef5cfb7-c75a-444a-87b5-b977ef5fa42e)
293+
294+
Finally solved 🥳!

0 commit comments

Comments
 (0)