leet 5 longest-palindrome-substring {string} {two_pointers}
TL;DR
- 길이 2, 3짜리 윈도우를 슬라이딩 하며 매 위치마다 투 포인터를 좌우로 확장하는 식으로 팰린드롬을 검사하면 된다. => 이렇게 했을때의 시간복잡도는 \(O(N^2)\)
- 길이 length짜리 윈도우를 슬라이딩 하면서 투 포인터를 가운대로 수축하는 형태로 팰린드롬을 검사하는 나의 방식은 시간복잡도가 \(O(N^3)\)가 나온다.
\(O(N^2)\) 짜리 코드
class Solution2:
def longestPalindrome(self, s: str) -> str:
"""
책의 설명에 따르면, 투 포인터가 중앙을 중심으로 확장하는 형태로 푼다.
나머지는 슬라이딩 윈도우로 체크하고, 홀수로 출발하는 케이스와 짝수로 출발하는 케이스 모두 다해서 max를 구한다.
🆗
195ms 17.42MB
왜 Solution1에 비해 빠르지? 둘 다 최악의 경우 N**2인 거 아닌가? 눈에 보이는 차이점이라곤 수축과 확장인데,
"""
def expand(l: int, r: int) -> str:
while l >= 0 and r < len(s) and s[l] == s[r]:
l -= 1
r += 1
return s[l + 1 : r]
if len(s) < 2 or s == s[::-1]:
return s
result = ""
for i in range(len(s) - 1):
result = max(result, expand(i, i + 1), expand(i, i + 2), key=len)
return result
\(O(N^3)\)짜리 코드
class Solution1_1:
def longestPalindrome(self, s: str) -> str:
"""
🆗
2931ms 17.38MB
바뀐거:
- palindrome 검사할때 문자열 슬라이스가 아니라 인덱스만 넘겨줬다.
- 원래부터 palindrome인 문자열은 그대로 리턴하는 코드가 추가됐다
Solution2와의 차이점은 윈도우의 크기를 2/3에서 시작하느냐, length에서 시작하느냐에 있다. 윈도우의 크기를 확장하는 방향은
중복을 없애고 이전 결과를 그대로 활용할 수 있다.
하지만 Solution1과 1_1의 문제풀이는 이전 결과를 활용하지 못한다. 윈도우의 length를 먼저 정하고 슬라이딩 하면서 각각에 대하여
검사하는 방법은 같은 인덱스의 중심을 가진 부분문자열을 여러번 다시 계산해야 하기 때문에 시간복잡도에 N을 더 곱하게 된다.
두 방식의 팰린드롬 검사시간은 O(N)으로 동일하지만 전체적인 복잡도를 보면 Solution 1과 1_1은 O(N**3)이 소요됐고, Solution2는
O(N**2)가 소요돼 여기에서 시간차가 발생한 것이다.
"""
def is_palindrome(l: int, r: int) -> bool:
while l < r:
if s[l] != s[r]:
return False
l += 1
r -= 1
return True
if len(s) < 2 or s == s[::-1]:
return s
for length in range(len(s), 1, -1):
for start in range(len(s) - length + 1):
end = start + length
if is_palindrome(start, end - 1):
return s[start:end]
return s[0]