Skip to content

Commit e2661f1

Browse files
committed
add solution: longest-palindromic-substring
1 parent 1574698 commit e2661f1

File tree

1 file changed

+135
-0
lines changed

1 file changed

+135
-0
lines changed
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
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

Comments
ย (0)