|
| 1 | +''' |
| 2 | +# 5. Longest Palindromic Substring |
| 3 | +
|
| 4 | +DP ํ
์ด๋ธ์ ์ฌ์ฉํ์ฌ ํฐ๋ฆฐ๋๋กฌ ์ฌ๋ถ๋ฅผ ์ ์ฅํ๊ณ ์ต๋ ๊ธธ์ด๋ฅผ ์ฐพ๊ธฐ. |
| 5 | +''' |
| 6 | +class Solution: |
| 7 | + ''' |
| 8 | + TC: O(n^2) |
| 9 | + SC: O(n^2) |
| 10 | + ''' |
| 11 | + def longestPalindrome(self, s: str) -> str: |
| 12 | + n = len(s) |
| 13 | + if n == 1: |
| 14 | + return s |
| 15 | + |
| 16 | + start, length = 0, 1 |
| 17 | + dp = [[False] * n for _ in range(n)] # SC: O(n^2) |
| 18 | + |
| 19 | + # length 1, diagonal elements in 2d array |
| 20 | + for i in range(n): # TC: O(n) |
| 21 | + dp[i][i] = True |
| 22 | + |
| 23 | + # length 2, are two elements same |
| 24 | + for i in range(n - 1): |
| 25 | + if s[i] == s[i + 1]: |
| 26 | + dp[i][i + 1] = True |
| 27 | + start, length = i, 2 |
| 28 | + |
| 29 | + # length 3+ |
| 30 | + for word_length in range(3, n + 1): # TC: O(n^2) |
| 31 | + for i in range(n - word_length + 1): |
| 32 | + j = i + word_length - 1 |
| 33 | + |
| 34 | + if s[i] == s[j] and dp[i + 1][j - 1]: |
| 35 | + dp[i][j] = True |
| 36 | + start, length = i, word_length |
| 37 | + |
| 38 | + return s[start:start + length] |
| 39 | + |
| 40 | +''' |
| 41 | +## Check Leetcode Hints |
| 42 | +- How can we reuse a previously computed palindrome to compute a larger palindrome? |
| 43 | + - use dp table that stores isPalindrome computation. |
| 44 | +- If โabaโ is a palindrome, is โxabaxโ a palindrome? Similarly is โxabayโ a palindrome? |
| 45 | + - if it is palindrome, we only need to check the outermost chars, that wrapping our palindrome. |
| 46 | +- Palindromic checks can be O(1) by reusing prev computations. |
| 47 | + - DP! |
| 48 | +
|
| 49 | +## ๋์ ํ๋ก๊ทธ๋๋ฐ ํ์ด |
| 50 | +- s[i:j]์ ํฐ๋ฆฐ๋๋กฌ ์ฌ๋ถ๋ฅผ ์ ์ฅํ๊ธฐ ์ํด์๋ 2์ฐจ์ ๋ฐฐ์ด์ ์ฌ์ฉํ๋ ๊ฒ์ด ๊ฐํธํ๋ค. |
| 51 | +- ๊ธธ์ด๊ฐ 1์ธ ๊ฒฝ์ฐ, ํฐ๋ฆฐ๋๋กฌ |
| 52 | +- ๊ธธ์ด๊ฐ 2์ธ ๊ฒฝ์ฐ, ๋ ๋ฌธ์๊ฐ ๊ฐ๋ค๋ฉด ํฐ๋ฆฐ๋๋กฌ |
| 53 | +- 3 ์ด์์ dp ํ
์ด๋ธ์ ํ์ฉํ๋ฉฐ ํ์ธ |
| 54 | + - ex) ๊ธธ์ด๊ฐ 5์ธ ๋ฌธ์์ด์ ๋ค์๊ณผ ๊ฐ์ด ํ์ |
| 55 | + ``` |
| 56 | + len: 3, i: 0, j: 2 |
| 57 | + len: 3, i: 1, j: 3 |
| 58 | + len: 3, i: 2, j: 4 |
| 59 | + len: 4, i: 0, j: 3 |
| 60 | + len: 4, i: 1, j: 4 |
| 61 | + len: 5, i: 0, j: 4 |
| 62 | + ``` |
| 63 | +
|
| 64 | +## ํ๊ตฌ |
| 65 | +- can we use sliding window to optimize? |
| 66 | +์ฌ๋ผ์ด๋ฉ ์๋์ฐ๋ ์ฐ์์ ์ธ ๊ตฌ๊ฐ์ ์ ์งํ๋ฉฐ ์ต์ ํด๋ฅผ ์ฐพ์ ๋ ์ ์ฉํ์ง๋ง, ํฐ๋ฆฐ๋๋กฌ์ ์ค์ ํ์ฅ๊ณผ ์ด์ ๊ณ์ฐ ์ฌ์ฌ์ฉ์ด ํต์ฌ. |
| 67 | +
|
| 68 | +- can we solve this by counting char? |
| 69 | +๋ฌธ์ ๊ฐ์๋ฅผ ์ธ๋ฉด "์ด๋ค ๋ฌธ์๊ฐ ๋ช ๋ฒ ๋์๋์ง"๋ง ์ ์ ์์ง๋ง, ํฐ๋ฆฐ๋๋กฌ ์ฌ๋ถ๋ ๋ฌธ์ ์์๊ฐ ์ค์ํ๋ค. |
| 70 | +
|
| 71 | +- ๊ทธ๋ ๋ค๋ฉด ๊ฐ์ ๋ฐฉ๋ฒ์? |
| 72 | +> Manacher's Algorithm: https://www.geeksforgeeks.org/manachers-algorithm-linear-time-longest-palindromic-substring-part-1/ |
| 73 | +> |
| 74 | +>ํฐ๋ฆฐ๋๋กฌ์ ๋์นญ์ฑ์ ํ์ฉํด ์ค์์ ๊ธฐ์ค์ผ๋ก ํ์ฅํ๋ Manacher's Algorithm์ ์ ์ฉํ ์ ์๋ค. ๋ชจ๋ ๋ฌธ์ ์ฌ์ด์ #์ ๋ฃ์ด ์ง์, ํ์๋ฅผ ๋์ผํ๊ฒ ๋ค๋ฃจ๋ ๊ธฐ๋ฒ์ธ๋ฐ ๊ตฌํ์ด ๋ค์ ๋ณต์กํ๋ค. ์๊ฐ ๋ณต์ก๋๋ O(n)์ด๋ค. |
| 75 | +''' |
| 76 | +class Solution: |
| 77 | + ''' |
| 78 | + TC: O(n) |
| 79 | + SC: O(n) |
| 80 | + ''' |
| 81 | + def longestPalindromeManacher(self, s: str) -> str: |
| 82 | + ''' |
| 83 | + 1. ๋ฌธ์์ด ๋ณํํ๊ธฐ (๊ฐ์ด๋ฐ์ # ์ถ๊ฐ) |
| 84 | + 2. ์ ์ฅ๊ณต๊ฐ ์ด๊ธฐํ |
| 85 | + 3. ๋ณํ๋ ๋ฌธ์์ด ์ํํ๊ธฐ |
| 86 | + (a) ๋ฏธ๋ฌ ์ธ๋ฑ์ค ๊ณ์ฐ |
| 87 | + (b) ํฐ๋ฆฐ๋๋กฌ ๋ฐ์ง๋ฆ ํ์ฅ |
| 88 | + - i์์ ์์ชฝ ๋ฌธ์๋ค์ ๋น๊ตํด์ ํ์ฅ |
| 89 | + (c) ์๋ก์ด ํฐ๋ฆฐ๋๋กฌ ์ฐพ๊ธฐ |
| 90 | + - ์๋ก์ด ํฐ๋ฆฐ๋๋กฌ์ i๋ก ์ค์ |
| 91 | + - ์๋ก์ด ํฐ๋ฆฐ๋๋กฌ์ ์ค๋ฅธ์ชฝ ๋์ i + p[i]๋ก ์ค์ |
| 92 | + (d) ๊ฐ์ฅ ๊ธด ํฐ๋ฆฐ๋๋กฌ ์
๋ฐ์ดํธ |
| 93 | + ''' |
| 94 | + transformed = '#' + '#'.join(s) + '#' |
| 95 | + n = len(transformed) |
| 96 | + p = [0] * n # i ์ค์ฌ์ ํฐ๋ฆฐ๋๋กฌ ํฌ๊ธฐ |
| 97 | + c = 0 # ํ์ฌ ํฐ๋ฆฐ๋๋กฌ์ ์ค์ฌ |
| 98 | + r = 0 # ํ์ฌ ํฐ๋ฆฐ๋๋กฌ์ ์ค๋ฅธ์ชฝ ๋ |
| 99 | + max_len = 0 # ๊ฐ์ฅ ๊ธด ํฐ๋ฆฐ๋๋กฌ์ ๊ธธ์ด |
| 100 | + center = 0 # ๊ฐ์ฅ ๊ธด ํฐ๋ฆฐ๋๋กฌ์ ์ค์ฌ |
| 101 | + |
| 102 | + for i in range(n): |
| 103 | + # ํ์ฌ ์์น i์ ๋ฏธ๋ฌ ์ธ๋ฑ์ค (์ค์์ ๊ธฐ์ค์ผ๋ก ๋์นญ๋๋ ์ธ๋ฑ์ค) |
| 104 | + # ex) ababbaa |
| 105 | + # 0123456 |
| 106 | + # i๊ฐ 3์ด๊ณ center๊ฐ 2์ผ ๋, 2*2 - 3 = 1, ๋ฏธ๋ฌ ์ธ๋ฑ์ค๋ 1 |
| 107 | + # i๊ฐ 5์ด๊ณ center๊ฐ 4์ผ ๋, 2*4 - 5 = 3, ๋ฏธ๋ฌ ์ธ๋ฑ์ค๋ 3 |
| 108 | + mirror = 2 * c - i |
| 109 | + |
| 110 | + if i < r: |
| 111 | + # r - i: ์ผ๋ง๋ ๋ ํ์ฅ ๋ ์ ์๋๊ฐ => ํ์ฌ ํฐ๋ฆฐ๋๋กฌ์ ์ค๋ฅธ์ชฝ ๋์์ ํ์ฌ ์ธ๋ฑ์ค๊น์ง์ ๊ฑฐ๋ฆฌ |
| 112 | + # p[mirror]: ๋ฏธ๋ฌ ์ธ๋ฑ์ค์์์ ํฐ๋ฆฐ๋๋กฌ ๋ฐ์ง๋ฆ |
| 113 | + p[i] = min(r - i, p[mirror]) |
| 114 | + # ์์ ๊ฐ๋งํผ๋ง ํ์ฅ์ด ๊ฐ๋ฅํ๋ค. ์๋ฅผ ๋ค์ด, r - i๊ฐ ๋ ์์ ๊ฐ์ด๋ผ๋ฉด ํฐ๋ฆฐ๋๋กฌ์ ๊ทธ๋งํผ๋ง ํ์ฅํ ์ ์๊ณ , p[mirror]๊ฐ ๋ ์์ ๊ฐ์ด๋ผ๋ฉด ์ด๋ฏธ ๊ทธ๋งํผ ํ์ฅ์ด ๋ ์ํ |
| 115 | + # r - i๊ฐ 3์ด๊ณ p[mirror]๊ฐ 2๋ผ๋ฉด ํฐ๋ฆฐ๋๋กฌ์ 2๋งํผ ํ์ฅํ ์ ์๊ณ , r - i๊ฐ 2์ด๊ณ p[mirror]๊ฐ 3์ด๋ผ๋ฉด ํฐ๋ฆฐ๋๋กฌ์ 2๋งํผ ํ์ฅํ ์ ์๋ค. |
| 116 | + |
| 117 | + # ํ์ฌ ์ค์ฌ์์ ํฐ๋ฆฐ๋๋กฌ์ ํ์ฅ |
| 118 | + # ์ ๋์ด ๊ฐ๋ค๋ฉด ํฐ๋ฆฐ๋๋กฌ ๋ฐ์ง๋ฆ p[i]๋ฅผ 1์ฉ ์ฆ๊ฐ |
| 119 | + while i + p[i] + 1 < n and i - p[i] - 1 >= 0 and transformed[i + p[i] + 1] == transformed[i - p[i] - 1]: |
| 120 | + p[i] += 1 |
| 121 | + |
| 122 | + # ๊ธฐ์กด์ ์ฐพ์ ํฐ๋ฆฐ๋๋กฌ๋ณด๋ค ๋ ํฐ ํฐ๋ฆฐ๋๋กฌ์ ์ฐพ์ ๊ฒฝ์ฐ |
| 123 | + # ํ์ฌ ์ค์ฌ๊ณผ ์ค๋ฅธ์ชฝ ๋์ ์ค์ |
| 124 | + if i + p[i] > r: |
| 125 | + c = i |
| 126 | + r = i + p[i] |
| 127 | + |
| 128 | + # ๊ฐ์ฅ ๊ธด ํฐ๋ฆฐ๋๋กฌ ์
๋ฐ์ดํธ |
| 129 | + if p[i] > max_len: |
| 130 | + max_len = p[i] |
| 131 | + center = i |
| 132 | + |
| 133 | + # ๋ณํ๋ ๋ฌธ์์ด์์ ๊ฐ์ฅ ๊ธด ํฐ๋ฆฐ๋๋กฌ์ ์๋ ๋ฌธ์์ด์์ ์ถ์ถ |
| 134 | + start = (center - max_len) // 2 |
| 135 | + return s[start:start + max_len] |
0 commit comments