|
1 | 1 | # [Problem 564: Find the Closest Palindrome](https://leetcode.com/problems/find-the-closest-palindrome/description/?envType=daily-question)
|
2 | 2 |
|
3 | 3 | ## Initial thoughts (stream-of-consciousness)
|
| 4 | +- One interesting aspect of the problem is that `n` is limited to at most 18 digits. So I'm guessing the algorithm will be $O(\mathrm{len}(n)^x)$, where $x$ is potentially something large. |
| 5 | +- Off the top of my head, I can see a few categories of scenarios we might encounter: |
| 6 | + - If `n` is *already* a palindrome, then we need to find a *different* palindrome. |
| 7 | + - If `len(n)` is odd, we can just add or subtract 1 (as allowed) to the middle digit and see which is closer to `n` (or if they're equally close, just subtract 1). |
| 8 | + - If `len(n)` is even, it's trickier. I guess we could add or subtract 1 to the *two* middle digits (again, as allowed) |
| 9 | + - If `n` is *not* a palindrome, then: |
| 10 | + - If `len(n)` is odd, maybe we just return `n[:len(n)//2 + 1] + n[:len(n)//2][::-1]`? |
| 11 | + - And if `len(n)` is even, we could return `n[:len(n)//2] + n[:len(n)//2][::-1]`? |
| 12 | +- The problem must not be this simple. Let's make something up...suppose `n = "293847"`. Since `n` is not a palindrome, and `len(n)` is even, is `293392` the closest palindrome? The difference is 455. What about 294492? The difference is 645, which is larger. But maybe there's a scenario where increasing or decreasing the middle digits might be helpful? I don't think it could hurt to check. It'd still be a very simple/fast solution. |
| 13 | +- What if `n = 9283743`? Then, using the above procedure, we'd return 9283829 (diff = 86). What about 9284829? (diff = 1086, which is larger). Or 9282829? (diff = 914, which is also larger than 86). |
| 14 | +- One scenario we'll need to cover is if `len(n) == 1`. If `n == '0'` then we just return `'1'`. Otherwise we should return `str(int(n) - 1)`. |
| 15 | +- Hmm. Well...I suppose we can just try this and see what happens? We'll need to test with a bunch of examples. |
4 | 16 |
|
5 | 17 | ## Refining the problem, round 2 thoughts
|
| 18 | +- First we'll need to check if `n` is a palindrome. We could use: |
| 19 | +```python |
| 20 | +def is_palindrome(n): |
| 21 | + return n[:len(n) // 2] == n[-(len(n) // 2):][::-1] |
| 22 | +``` |
| 23 | + - Actually, this was easier than I thought it'd be-- we don't need to differentiate from even vs. odd-length `n`s, since the middle digit doesn't matter if `len(n)` is odd (it doesn't affect the "palindrome" status of `n`). |
| 24 | +- We can also write a convenience function to check distances between string representations of different numbers: |
| 25 | +```python |
| 26 | +def dist(a, b): |
| 27 | + return abs(int(a) - int(b)) |
| 28 | +``` |
| 29 | +- And also some code for incrementing or decrementing the middle digit(s): |
| 30 | +```python |
| 31 | +def wiggle_middle_digits(n): |
| 32 | + x1 = n.copy() |
| 33 | + x2 = n.copy() |
| 34 | + |
| 35 | + x1_middle = str(int(n[len(n) // 2]) - 1) |
| 36 | + x2_middle = str(int(n[len(n) // 2]) + 1) |
| 37 | + |
| 38 | + if len(n) % 2 == 0: |
| 39 | + x1[len(n) // 2] = x1_middle |
| 40 | + x1[len(n) // 2 + 1] = x1_middle |
| 41 | + x2[len(n) // 2] = x2_middle |
| 42 | + x2[len(n) // 2 + 1] = x2_middle |
| 43 | + else: |
| 44 | + x1[len(n) // 2 + 1] = x1_middle |
| 45 | + x2[len(n) // 2 + 1] = x2_middle |
| 46 | + |
| 47 | + if dist(n, x1) <= dist(n, x2): |
| 48 | + return x1 |
| 49 | + else: |
| 50 | + return x2 |
| 51 | +``` |
| 52 | + |
| 53 | +- Other than that, we just need to implement the above rules. Let's see if it works! |
6 | 54 |
|
7 | 55 | ## Attempted solution(s)
|
8 | 56 | ```python
|
9 |
| -class Solution: # paste your code here! |
10 |
| - ... |
| 57 | +from copy import copy |
| 58 | + |
| 59 | +class Solution: |
| 60 | + def nearestPalindromic(self, n: str) -> str: |
| 61 | + def is_palindrome(n): |
| 62 | + return n[:len(n) // 2] == n[-(len(n) // 2):][::-1] |
| 63 | + |
| 64 | + def dist(a, b): |
| 65 | + return abs(int(a) - int(b)) |
| 66 | + |
| 67 | + def wiggle_middle_digits(n): |
| 68 | + x1 = list(copy(n)) |
| 69 | + x2 = list(copy(n)) |
| 70 | + |
| 71 | + x1_middle = str(int(n[len(n) // 2]) - 1) |
| 72 | + x2_middle = str(int(n[len(n) // 2]) + 1) |
| 73 | + |
| 74 | + if len(n) % 2 == 0: |
| 75 | + x1[len(n) // 2] = x1_middle |
| 76 | + x1[len(n) // 2 + 1] = x1_middle |
| 77 | + x2[len(n) // 2] = x2_middle |
| 78 | + x2[len(n) // 2 + 1] = x2_middle |
| 79 | + else: |
| 80 | + x1[len(n) // 2 + 1] = x1_middle |
| 81 | + x2[len(n) // 2 + 1] = x2_middle |
| 82 | + |
| 83 | + x1 = ''.join(x1) |
| 84 | + x2 = ''.join(x2) |
| 85 | + |
| 86 | + if dist(n, x1) <= dist(n, x2): |
| 87 | + return x1 |
| 88 | + else: |
| 89 | + return x2 |
| 90 | + |
| 91 | + if len(n) == 1: |
| 92 | + if n == "0": |
| 93 | + return "1" |
| 94 | + else: |
| 95 | + return str(int(n) - 1) |
| 96 | + |
| 97 | + if is_palindrome(n): |
| 98 | + return wiggle_middle_digits(n) |
| 99 | + elif len(n) % 2 == 0: |
| 100 | + return n[:len(n)//2] + n[:len(n)//2][::-1] |
| 101 | + else: |
| 102 | + return n[:len(n)//2 + 1] + n[:len(n)//2][::-1] |
11 | 103 | ```
|
| 104 | +- Both given test cases pass |
| 105 | +- Let's try a bunch of other examples: |
| 106 | + - `n = "32459827345987"`: pass |
| 107 | + - `n = "4387348756345786"`: pass |
| 108 | + - `n = "438734878437834": fail! (note: I also had to fix up the "wiggle" code syntax). |
| 109 | + - 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! |
0 commit comments