IT/자기계발 ( Leetcode )

2021-01-19] Longest Palindromic Substring

Bell_bear 2021. 1. 19. 23:07
반응형

오늘의 문제:

leetcode.com/explore/challenge/card/january-leetcoding-challenge-2021/581/week-3-january-15th-january-21st/3609/

 

Explore - LeetCode

LeetCode Explore is the best place for everyone to start practicing and learning on LeetCode. No matter if you are a beginner or a master, there are always new topics waiting for you to explore.

leetcode.com

 

Palindromic substring.. 구글 번역으로 회문 부분문자열.. 무슨 말인지 처음에 이해를 못했다. ( 영어실력의 부족.. )

그냥 '일요일', '토마토', '이효리' 마냥 앞으로 읽어도, 뒤로 읽어도 같은 문자열을 의미하는 것이다.

 

 

문제 예시 1-4

주어지는 문자열 s에 대해서 위에서 언급한 회문 부분문자열 중 가장 긴 값을 구하는 문제이다.

 

예시 1번처럼 'babad' 는 답이라고 할수있는 값이 'bab','aba'  두 개이다. 이 중 아무거나 반환해주면 된다.

 

문자열이 1개(예시 4)이거나 회문 부분문자열이 없는 경우(예시 5) 첫번째 문자를 반환해주면 된다.

 

주어진 예시에서 없는 내용의 예시로 하나를 들자면,

 

'abaaba' 가 주어진다면, 회문 부분문자열은 총 4개 'aa','aba','baab','abaaba'가 될 것이고,

답으로 가장 긴 값인 'abaaba'를 반환하면 된다.

 

위의 예제로 좀 더 코딩에 가깝게 고민해본다면, 회문 부분문자열은 앞뒤의 문자가 동일해야한다.

 

첫번째 값 'a'로 회문부분문자열이 만들어질려면, 우선 'a'가 또 있어야만 한다. 즉 'a' 와 'baaba'에서

'a' 가 존재하는 지를 판단해야한다. 만약 존재하지 않는다면 'a'로 만들수 있는 회문 부분문자열은 없다.

if 'a' in 'baaba':
    # 회문 부분문자열 검사 가능
else:
    # 할필요 없으니 pass

그리고 'a'가 여러 개가 존재할 수 있으므로, 모든 부분에 대해서 검사해야한다.

( 'aba' , 'abaa', 'abaaba' ) 해당 문자열이 회문 부분문자열이 될 수 있는지 검사해야하는 대상이다.

 

모든 부분에 대해서 검사해야하지만, 우리는 가장 큰 값을 원하기 때문에 맨 뒤에서 부터 검사하여,

값이 구해진다면 앞의 작은 문자열들은 검사할 필요가 없어진다. ( 코딩에서 종단조건으로 쓰여진다. )

 

res_list = [idx for idx, value in enumerate('baaba') if value == 'a']
# 'a'를 가지고 있는 index 값을 List로 만들어 주는 코드
res_list.reverse()
# 만들어진 List를 역순으로 만들어야 맨 뒤에서 부터 검사할 수 있다.

 

이제 'abaaba' 를 먼저 회문 부분문자열 검사를 한다.

 

s='abaaba'
first=0
last=5
while first < last : # 종단조건 
    if s[first] == s[last]: # 앞의 값과 뒤의 값이 같아야만 회문 부분문자열에 성립
        first+=1 
        last-=1 # first,last 한 칸씩 앞으로,뒤로 옮긴다.
    else: # 같지 않으면 회문 부분문자열이 아니므로 더이상 검사하지 않는다.
        break
if first>=last: # 회문부분문자열이 맞다면 break에 걸리지 않고 종단조건에 걸린다.
    ret = s[0:5] # 'abaaba'는 회문 부분문자열이 맞으므로 저장해준다.

해당 while문을 끝까지 돈다면, 회문 부분문자열이 되고 first>= last 조건이 성립하게된다. 이를 저장해준다.

 

그 후 두 번째로 큰 문자열을 검사해야하지만, 가장 큰 회문 부분문자열이 될 수 있는 문자열에서 값이 나왔으므로,

아래는 더 검사할 필요가 없겠다.

 

위의 코드들을 합쳐서 만든 코드는 아래와 같다.

 

문제풀이)

 

class Solution:
    def longestPalindrome(self, s: str) -> str:
        ret=""
        if len(s)==1: #주어진 string이 하나면 그대로 반환
            return s
        elif len(s)==2: # string이 두개면 바로 비교 후 반환
            if s[0]==s[1]:
                return s
            else:
                return s[0]
        else:
            for i in range(len(s)):
                if len(ret) >len(s[i:]): 
                    # 이미 저장한 회문부분문자열의 길이가 새로 비교할 전체 문자열보다 길다면 검사할 필요 없음
                    break
                if s[i] in s[i+1:]:
                    res_list = [idx for idx, value in enumerate(s[i+1:]) if value == s[i]]
                    res_list.reverse()
                    for d in res_list:
                        first=i
                        last=d+i+1
                        if len(ret) > len(s[first:last+1]):
                            # 구한 회문부분문자열 길이가 더 크면 검사할 필요 없음
                            break
                        while first < last : # 회문 부분문자열 검사 코드
                            if s[first] == s[last]:
                                first+=1
                                last-=1
                            else:
                                break
                        if first>=last:
                            if len(s[i:d+i+1+1]) > len(ret): # 이번에 구한 회문 부분문자열 값이 기존 값보다 크면 변경
                                ret=s[i:d+i+1+1]
                else:
                    continue
        if ret=="": # 회문 부분 문자열이 없을 경우 첫번째 값 반환
            return s[0]
        return ret

 

해당 결과는 성공했지만, 성능이 좋진 않았다.

실행시간이 너무 긴 것을 보면, 좀 더 줄일 수 있는 방법을 고려해야 할 것 같다.

 

실행시간이 짧은 코드를 보니 for 한번으로 돌려버린 저런 멋진 코드.. 최고다..

 

string == string[::-1] # 회문 부분문자열을 검사하는 코드.. 나의 python 무지함이 드러나는 킬포..

class Solution:
    
    def longestPalindrome(self, string: str) -> str:
        if len(string) <= 1 or string == string[::-1]:
            return string

        start = 0
        length = 1

        for i in range(len(string)):
            odd = string[i - length - 1 : i + 1]
            even = string[i - length : i + 1]

            if 0 <= i - length - 1 and odd == odd[::-1]:
                start = i - length - 1
                length += 2
            elif 0 <= i - length and even == even[::-1]:
                start = i - length
                length += 1

        return string[start : start + length]
반응형