Skip to content

Commit 41cd764

Browse files
my solution for 564
1 parent 8146395 commit 41cd764

File tree

1 file changed

+207
-0
lines changed

1 file changed

+207
-0
lines changed

problems/564/jeremymanning.md

+207
Original file line numberDiff line numberDiff line change
@@ -107,3 +107,210 @@ class Solution:
107107
- `n = "4387348756345786"`: pass
108108
- `n = "438734878437834": fail! (note: I also had to fix up the "wiggle" code syntax).
109109
- There seems to be an issue with the `wiggle_middle_digits` code-- it doesn't seem to be working as expected. However, I'm out of time for tonight, so I'll have to come back to this!
110+
111+
## Coming back to this...
112+
- I'm debugging the `wiggle_middle_digits` code, and I notice a few issues:
113+
- My indexing is off by 1 (the "changing" part should be):
114+
```python
115+
if len(n) % 2 == 0:
116+
x1[len(n) // 2 - 1] = x1_middle
117+
x1[len(n) // 2] = x1_middle
118+
x2[len(n) // 2 - 1] = x2_middle
119+
x2[len(n) // 2] = x2_middle
120+
else:
121+
x1[len(n) // 2] = x1_middle
122+
x2[len(n) // 2] = x2_middle
123+
```
124+
- Here's the updated code:
125+
```python
126+
from copy import copy
127+
128+
class Solution:
129+
def nearestPalindromic(self, n: str) -> str:
130+
def is_palindrome(n):
131+
return n[:len(n) // 2] == n[-(len(n) // 2):][::-1]
132+
133+
def dist(a, b):
134+
return abs(int(a) - int(b))
135+
136+
def wiggle_middle_digits(n):
137+
x1 = list(copy(n))
138+
x2 = list(copy(n))
139+
140+
x1_middle = str(int(n[len(n) // 2]) - 1)
141+
x2_middle = str(int(n[len(n) // 2]) + 1)
142+
143+
if len(n) % 2 == 0:
144+
x1[len(n) // 2 - 1] = x1_middle
145+
x1[len(n) // 2] = x1_middle
146+
x2[len(n) // 2 - 1] = x2_middle
147+
x2[len(n) // 2] = x2_middle
148+
else:
149+
x1[len(n) // 2] = x1_middle
150+
x2[len(n) // 2] = x2_middle
151+
152+
x1 = ''.join(x1)
153+
x2 = ''.join(x2)
154+
155+
if dist(n, x1) <= dist(n, x2):
156+
return x1
157+
else:
158+
return x2
159+
160+
if len(n) == 1:
161+
if n == "0":
162+
return "1"
163+
else:
164+
return str(int(n) - 1)
165+
166+
if is_palindrome(n):
167+
return wiggle_middle_digits(n)
168+
elif len(n) % 2 == 0:
169+
return n[:len(n)//2] + n[:len(n)//2][::-1]
170+
else:
171+
return n[:len(n)//2 + 1] + n[:len(n)//2][::-1]
172+
```
173+
- Now this test case passes.
174+
- Let's try submitting...
175+
176+
![Screenshot 2024-08-24 at 4 01 56 PM](https://github.com/user-attachments/assets/a824a5ad-935d-4e3a-b7f5-ca510f9343ba)
177+
178+
- Ah. I forgot to account for this case (when `n = "10"` the correct answer should be `"9"`, not `"11"`).
179+
- In general, there could be a whole range of these sorts of cases (not just when there are only two digits). E.g., when `n = "100000"` we should output `"99999"`.
180+
- I think we could handle this with one more check: if `is_palindrome(str(int(n) - 1))` then return that:
181+
```python
182+
from copy import copy
183+
184+
class Solution:
185+
def nearestPalindromic(self, n: str) -> str:
186+
def is_palindrome(n):
187+
return len(n) == 1 or n[:len(n) // 2] == n[-(len(n) // 2):][::-1]
188+
189+
def dist(a, b):
190+
return abs(int(a) - int(b))
191+
192+
def wiggle_middle_digits(n):
193+
x1 = list(copy(n))
194+
x2 = list(copy(n))
195+
196+
x1_middle = str(int(n[len(n) // 2]) - 1)
197+
x2_middle = str(int(n[len(n) // 2]) + 1)
198+
199+
if len(n) % 2 == 0:
200+
x1[len(n) // 2 - 1] = x1_middle
201+
x1[len(n) // 2] = x1_middle
202+
x2[len(n) // 2 - 1] = x2_middle
203+
x2[len(n) // 2] = x2_middle
204+
else:
205+
x1[len(n) // 2] = x1_middle
206+
x2[len(n) // 2] = x2_middle
207+
208+
x1 = ''.join(x1)
209+
x2 = ''.join(x2)
210+
211+
if dist(n, x1) <= dist(n, x2):
212+
return x1
213+
else:
214+
return x2
215+
216+
if len(n) == 1:
217+
if n == "0":
218+
return "1"
219+
else:
220+
return str(int(n) - 1)
221+
222+
if is_palindrome(str(int(n) - 1)):
223+
return str(int(n) - 1)
224+
225+
if is_palindrome(n):
226+
return wiggle_middle_digits(n)
227+
elif len(n) % 2 == 0:
228+
return n[:len(n)//2] + n[:len(n)//2][::-1]
229+
else:
230+
return n[:len(n)//2 + 1] + n[:len(n)//2][::-1]
231+
```
232+
- Now `n = "10"` and `n = "1000000"` both pass; re-submitting...
233+
234+
![Screenshot 2024-08-24 at 4 08 00 PM](https://github.com/user-attachments/assets/a668f936-01f1-48d3-8c39-7a0dab6e5583)
235+
236+
- Oof 🤦. Right...`"00"` isn't a valid output, but the `"1"'s in `"11"` are technically the "middle digits".
237+
- There are a bunch of these sorts of edge cases. I think we actually need to generate a *set* of candidates, and then pick the closest (or smallest + closest if there's a tie):
238+
- First try mirroring the left half of the string
239+
- Also try wiggling the middle digit(s)
240+
- Check if we're in a "close to a power of 10" scenario. We can just add `"9" * (len(n) - 1)` and `"1" + "0" * (len(n) - 1) + "1"` to the set of candidates manually
241+
- In case any of these have reproduced the original number, remove it
242+
- If we have empty strings, all zeros, etc., we'll remove those too
243+
- Of the remaining candidates, find the closest to `n` that is also the smallest-- we can do this by returning `min(candidates, key=lambda x: (dist(n, x), int(x)))`
244+
- Updated solution:
245+
```python
246+
class Solution:
247+
def nearestPalindromic(self, n: str) -> str:
248+
def is_palindrome(x):
249+
return x == x[::-1]
250+
251+
def dist(a, b):
252+
return abs(int(a) - int(b))
253+
254+
def wiggle_middle_digits(n):
255+
x1 = list(n)
256+
x2 = list(n)
257+
258+
mid_index = len(n) // 2
259+
if len(n) % 2 == 0:
260+
left_part = n[:mid_index]
261+
x1_middle = str(int(left_part) - 1)
262+
x2_middle = str(int(left_part) + 1)
263+
x1 = x1_middle + x1_middle[::-1]
264+
x2 = x2_middle + x2_middle[::-1]
265+
else:
266+
left_part = n[:mid_index + 1]
267+
x1_middle = str(int(left_part) - 1)
268+
x2_middle = str(int(left_part) + 1)
269+
x1 = x1_middle + x1_middle[:-1][::-1]
270+
x2 = x2_middle + x2_middle[:-1][::-1]
271+
272+
# new special cases (e.g., 999 vs. 1001)
273+
if len(x1) < len(n) or x1 == '':
274+
x1 = "9" * (len(n) - 1)
275+
if len(x2) > len(n) or x2 == '':
276+
x2 = "1" + "0" * (len(n) - 1) + "1"
277+
278+
return (x1, x2)
279+
280+
if n == "1":
281+
return "0"
282+
283+
candidates = set()
284+
285+
# mirroring
286+
if len(n) % 2 == 0:
287+
mid_index = len(n) // 2
288+
mirrored = n[:mid_index] + n[:mid_index][::-1]
289+
else:
290+
mid_index = len(n) // 2
291+
mirrored = n[:mid_index + 1] + n[:mid_index][::-1]
292+
293+
candidates.add(mirrored)
294+
295+
# wiggling
296+
wiggle_candidates = wiggle_middle_digits(n)
297+
candidates.add(wiggle_candidates[0])
298+
candidates.add(wiggle_candidates[1])
299+
300+
# edge cases (for when we're "near" a power of 10)
301+
candidates.add("9" * (len(n) - 1))
302+
candidates.add("1" + "0" * (len(n) - 1) + "1")
303+
304+
# remove bad candidates (including the original number)
305+
candidates.discard(n)
306+
candidates = {c for c in candidates if c and c.isdigit()}
307+
308+
# return the closest/smallest
309+
return min(candidates, key=lambda x: (dist(n, x), int(x)))
310+
```
311+
- Test cases pass, including previously failing edge cases
312+
- Submitting... 🤞
313+
314+
![Screenshot 2024-08-24 at 4 23 14 PM](https://github.com/user-attachments/assets/2f803edd-b809-4a60-9a4e-871e2a461bdd)
315+
316+
- Solved!

0 commit comments

Comments
 (0)