๐ ๋ชฉ์ฐจ
๐ค ๋ฌธ์ : Longest Palindromic Substring | LeetCode 780
๋ฌธ์ : https://leetcode.com/explore/interview/card/top-interview-questions-medium/103/array-and-strings/780/
palindorme : ๊ฑฐ๊พธ๋ก ์ฝ์ด๋ ์ ๋๋ก ์ฝ๋ ๊ฒ๊ณผ ๊ฐ์ ๋ฌธ์ฅ์ด๋ ๋ฑ๋ง, ์ซ์, ๋ฌธ์์ด(sequence of characters)
์ฃผ์ด์ง ๋ฌธ์์ด์์ ๊ฐ์ฅ ๊ธด palindrome์ ์ฐพ๋ ๋ฌธ์ ์ ๋๋ค.
palindrome์ ํญ์ ํ์ palindrome ์ง์ palindrome ๋๊ฐ์ง ์ผ์ด์ค ๋ชจ๋ ๊ณ ๋ คํด์ผ ํฉ๋๋ค.!
ํ์ palindrome: babad
์ง์ palindrome: cbbd
๐ก ํ์ด
1. ๋ฌธ์์ด์ ์ํํ๋ฉฐ ํด๋น ๋ฌธ์๋ฅผ ์ค๊ฐ๋ฌธ์๋ก ํ๋ ๊ฐ์ฅ ๊ธด palindrome ์ฐพ๊ธฐ
๊ฐ์ฅ ์ฒ์์ผ๋ก ์ ๊ทผํ ํ์ด์ ๋๋ค.
ํน์ ๋ฌธ์๋ฅผ ์ค์ฌ์ผ๋ก ๊ฐ์ฅ ๊ธด palindrome์ ์ฐพ๋ getPalindrome์ด๋ผ๋ ํจ์๋ฅผ ๋ง๋ค์๊ณ ,
์ฃผ์ด์ง ๋ฌธ์์ด์ ์ํํ๋ฉฐ palindrome์ ํธ์ถํ์ต๋๋ค.
๋ถ๋ถ๋ฌธ์์ด(substring)์ ๋ค๋ฃจ๋ ํจ์๋ฅผ ๋ง๋ค ๋๋,
๋ฌธ์์ด์ ์ง์ ๋ง๋ค์ด์ ์ ๋ฌํ๋ ๊ฒ ๋ณด๋ค index๋ง ์ฃผ๊ณ ๋ฐ๋๊ฒ ํจ์ฌ ๋น ๋ฆ ๋๋ค.
from typing import List
class Solution:
def longestPalindrome(self, s: str) -> str:
str_length = len(s)
def getPalindrome(idx1, idx2):
if idx1 < 0: return (idx1, idx2)
if idx2 >= str_length: return (idx1, idx2)
if s[idx1] is not s[idx2]: return (idx1, idx2)
return getPalindrome(idx1-1, idx2+1)
longest_palindrome_size = 0
longest_palindrome_pair = None
for idx in range(str_length): # [1]
(st, ed) = getPalindrome(idx, idx) # [2]
if ed-st-1 > longest_palindrome_size:
longest_palindrome_pair = (st, ed)
longest_palindrome_size = ed-st-1
(st, ed) = getPalindrome(idx, idx+1) # [3]
if ed-st-1 > longest_palindrome_size:
longest_palindrome_pair = (st, ed)
longest_palindrome_size = ed-st-1
return s[longest_palindrome_pair[0]+1:longest_palindrome_pair[1]]
[1] idx๋ฒ์งธ ๋ฌธ์๋ฅผ ๊ธฐ์ค์ผ๋ก palindrome์ ์ฐพ์ ๊ฒ
[2] ํ์ palindrome ex) cabad
[3] ์ง์ palindrome ex) cabbad
Time Complexity
๋ฌธ์์ด์ ๊ฐ character์ ๋ํด(n) palindrome์ธ์ง ์ฌ๋ถ๋ฅผ ํ์ธํ๋ฏ๋ก (n)
O(n^2) ์ ๋๋ค.
Space Complexity
๋ณ๋ ๊ณต๊ฐ์ ์ฌ์ฉํ์ง ์์ต๋๋ค.
2. 1์ ํ์ด๋ฅผ ๋ฌธ์์ด์ ๋ง๋๋ ๋ฐฉ์์ผ๋ก ๊ตฌํํ ๊ฒฝ์ฐ
๋ฌธ์์ด์ ๋ง๋ค์ด ์ ๋ฌํ๋ ๋ฐฉ์์ผ๋ก ๊ตฌํํ ์ฝ๋์ ๋๋ค.
Runtime์ด 5077ms์ผ๋ก 5๋ฐฐ๊ฐ๋ ์ค๋ ๊ฑธ๋ฆฌ๋ค์.
class Solution:
def longestPalindrome(self, s: str) -> str:
str_length = len(s)
def palindrome(idx1, idx2, curr_str):
if idx1 is idx2: return palindrome(idx1-1, idx2+1, curr_str+s[idx1])
if idx1 < 0: return curr_str
if idx2 >= str_length: return curr_str
if s[idx1] is not s[idx2]: return curr_str
return palindrome(idx1-1, idx2+1, s[idx1]+curr_str+s[idx2])
longest_palindrome = ""
for idx in range(str_length):
new_palindrome = palindrome(idx, idx, "")
longest_palindrome = new_palindrome if len(longest_palindrome) < len(new_palindrome) else longest_palindrome
new_palindrome = palindrome(idx, idx+1, "")
longest_palindrome = new_palindrome if len(longest_palindrome) < len(new_palindrome) else longest_palindrome
return longest_palindrome
3. dynamic programming
๋ ์ธ๋ฑ์ค ์ฌ์ด์ substring์ด palindrome์ธ์ง ์ฌ๋ถ๋ฅผ 2์ฐจ์ ๋ฐฐ์ด(cache)์ ๋ฐ๋ก ์ ์ฅํด๋๋ ๋ฐฉ์์ ๋๋ค.
dabac ์ ๊ฒฝ์ฐ cache๋ชจ์ต์ด ์๋์ ๊ฐ๊ฒ ๋ค์.
d | a | b | a | c | |
d | 1 | 0 | 0 | 0 | 0 |
a | 1 | 0 | 1 | 0 | |
b | 1 | 0 | 0 | ||
a | 1 | 0 | |||
c | 1 |
cache์ ๊ฐ์ ์ฑ์ฐ๋ ๋ก์ง์ ์๋์ ๊ฐ์ต๋๋ค.
- ๋๊ฐ์ ์์๋ ๋ชจ๋ 1
- ๋๊ฐ์ ์ผ์ชฝ ์๋ ์์๊ฐ1์ด๊ณ (์์, ๋ ๋ฌธ์์ด ์ ์ธ palindrome)
ํ์ฌ ๊ฐ๋ก์ถ๊ณผ ์ธ๋ก์ถ์ ๋ฌธ์๊ฐ ๊ฐ๋ค๋ฉด ์์, ๋ ๋ฌธ์๊ฐ ๊ฐ์) 1 - ๋๋จธ์ง๋ 0
def longestPalindrome(self, s: str) -> str:
n = len(s)
if n is 1: return s
cache = [[0] * n for _ in range(n)]
maxLength = 1
longest_index_pair = (0,0)
for i in range(n):
cache[i][i] = 1
for i in range(n-1):
if s[i] is s[i+1]:
if maxLength < 2:
maxLength = 2
longest_index_pair = (i, i+1)
cache[i][i+1] = 1
for i in range(2, n):
for j in range(n-i):
x = j
y = j+i
if s[x] is s[y] and cache[x+1][y-1]:
cache[x][y] = 1
if maxLength < i+1:
maxLength = i+1
longest_index_pair = (x, y)
return s[longest_index_pair[0]:longest_index_pair[1]+1]
Time Complexity
O(N^2)
Space Complexity
O(N^2)
Time Complexity๊ฐ ๋๊ฐ์ด N^2์ธ 1๋ฒ ํ์ด์ ๋นํด ์ํ์๊ฐ์ด ํจ์ฌ ์ค๋๊ฑธ๋ฆฌ๋ค์.
์๋ฌด๋๋ ์ค๊ฐ์ palindrome์ด ์๋๊ฒ ํ์คํ ์ผ์ด์ค๋ผ๋ ์ ์ฒด๋ฅผ ๋ค ๋ณด๊ธฐ ๋๋ฌธ์ธ ๊ฒ ๊ฐ์ต๋๋ค.
1์ worst case๊ฐ N^2์ด์๋ค๋ฉด, ์ด ํ์ด๋ ๋ฌด์กฐ๊ฑด N^2์ธ ๊ฑฐ์ฃ
4. ๋ ๋น ๋ฅด๊ฒ - ์ฐ์๋ ๋ฌธ์์ด์ด ๋์ฌ ๊ฒฝ์ฐ palindromeํ์ธ ์์ด skip
๋ค๋ฅธ ํ์ด๊ฐ ์์๊น discussion์ ๋ณด๋ค๊ฐ ์๊ฒ๋ ํ์ด์ธ๋ฐ, ๊ฐ๋จํ๊ณ ์ฐธ์ ํ์ฌ ๊ฐ์ด ์๊ฐํฉ๋๋ค.
์ด ํ์ด๋ ๊ฐ์ ๋ฌธ์๊ฐ ์ฐ์ํด์ ๋์ฌ ๊ฒฝ์ฐ์๋ ๋ค ์คํตํ๋ ๋ฐฉ์์ ๋๋ค.
์๊ฐํด๋ณด๋ฉด, ๊ฐ์ ๋ฌธ์๊ฐ ์ฐ์ํด์ ๋์จ๋ค๋ฉด ์ข์ฐ๋ฅผ 1:1๋ก ๊ตณ์ด ๋น๊ตํ ํ์๊ฐ ์๊ฒ ์ฃ .
worst case(๊ฐ์ ๋ฌธ์๊ฐ ์ฐ์ํด์ ๋์ค์ง ์๋ ๊ฒฝ์ฐ)๋ N^2์ผ๋ก ๊ฐ๊ฒ ์ง๋ง, ํ๊ท ์ํ์๋๋ ๋นจ๋ผ์ง๊ฒ ๋ค์.
# expand same characters
def longestPalindrome(self, s: str) -> str:
"""
The longest Palindrome including s[i] should contains substr s[l+1, r],
where all elements in s[l+1, r] are s[i]
This saves checks when a same char is repeated consecutively in s.
"""
p, idx = '', 0
while idx < len(s):
l = r = idx
# expand when having same ajacent chars
while l-1>=0 and s[l] == s[l-1]: l -= 1
while r+1<len(s) and s[r] == s[r+1]: r+=1
idx = r+1 # update idx, this can save checks
# expand to different chars
while l>=0 and r<len(s) and s[l] == s[r]:
l, r = l-1, r+1
# update p
p = max([p, s[l+1:r]], key = lambda x:len(x))
return p
ํ ์คํธ์ผ์ด์ค์ ์ฐ์๋ ๋ฌธ์์ด์ด ๋ง์๋์ง ์ํ์๋๊ฐ ๊ฑฐ์ 1/10์ผ๋ก ์ค์๋ค์!
palindrome์ด ์ข์ฐ ๋์นญ์ด๋ผ๋ ์๊ฐ์๋ง ๊ฐํ ์ด๋ฐ ์ฐธ์ ํ ์๊ฐ์ ๋ ์ฌ๋ฆฌ์ง ๋ชปํ๋๊ฒ ์์ฝ์ต๋๋ค. ใ ใ