LeetCode/DP심화

516. Longest Palindromic Subsequence ★★

hyunkookim 2025. 1. 4. 18:11

516. Longest Palindromic Subsequence

 

https://youtu.be/bUr8cNWI09Q?si=5OUyGmMcZAuUGtkC

 

1) LCS로 푸는 법

원래 문자열과 뒤집은 문자열의 LCS 는 Longest Palindromic Subsequence 임

 

Palindromic string: 읽었을때, 앞으로 읽으나 뒤로 읽으나 같은 문자열

LCS(Longest Common Subsequence): 두 문자열에서 순서를 유지하면서 가장 긴 공통 부분 수열을 찾는 문제를 말합니다.

 

  • 부분 수열 (Subsequence):
    • 문자열에서 몇 개의 문자를 순서를 유지하면서 선택한 부분 문자열.
    • 반드시 연속할 필요는 없지만, 문자들의 순서는 원래 문자열과 같아야 합니다.
    • 예:
      • 문자열 "ABCDEF"의 부분 수열: "ACE", "BDF", "ABC", "EF", 등.
  • 공통 부분 수열 (Common Subsequence):
    • 두 문자열에서 순서를 유지하면서 공통으로 포함된 부분 수열.
    • 예:
      • 문자열 "ABCDEF"와 "AEDNEK"의 공통 부분 수열: "AE", "AD", "A", 등.
  • 가장 긴 공통 부분 수열 (LCS):
    • 두 문자열의 모든 공통 부분 수열 중 가장 긴 수열.
    • 예:
      • 문자열 "ABCDEF"와 "AEDNEK"의 LCS는 "ADE".

 

class Solution:
    def longestCommonSubseq(self, s1: str, s2: str) -> int:
        # LCS 로 푸는 방법
        N, M = len(s1), len(s2)
        dp = [ [0] * (M+1) for _ in range(N+1) ]
        for i in range(N):
            for j in range(M):
                if s1[i] == s2[j]:
                    dp[i+1][j+1] = 1+dp[i][j]
                else:
                    dp[i+1][j+1] = max(dp[i+1][j], dp[i][j+1])
        return dp[N][M]

    def longestPalindromeSubseq(self, s: str) -> int:
        return self.longestCommonSubseq(s, s[::-1])

 

2) DP로 푸는 법

 

아래 코드는 타임 초과...

 

class Solution:
    def longestPalindromeSubseq(self, s: str) -> int:
        # DP로 푸는 방법, 브루스포스, 디시즌트리, 메모이제이션

        cache = {}

        def dfs(l, r):
            if l < 0 or r == len(s): # left, right 인덱스가 s의 범위를 벗어나면
                return 0

            if (l, r) in cache:
                return cache[(l, r)]

            if s[l] == s[r]: # charactor match
                # 글자는 같지만, 
                # l==r 인덱스 위치가 같으면, 같은 문자이므로 1개로,
                # l!=r 인덱스 위치가 다르면, 다른 문자이므로 2개로.
                length = 1 if l == r else 2 
                cache[(l, r)] = length + dfs(l-1, r+1) # 이제, 둘다 인덱스 늘리고, 검색
            else: # don't match
                cache[(l, r)] = max(dfs(l-1, r), dfs(l, r+1))

            return cache[(l, r)]

        for i in range(lens(s)):
            dfs(i, i) # odd length, 홀수 길이
            dfs(i, i+1) # even length, 짝수 길이

        return max(cache.values())

 

최종 코드

 

 

class Solution:
    def longestPalindromeSubseq(self, s: str) -> int:
        n = len(s)
        dp = [[0] * n for _ in range(n)]

        # 초기값: 부분 문자열 길이 1인 경우
        for i in range(n):
            dp[i][i] = 1

        # DP 테이블 채우기 (길이 2 이상 부분 문자열)
        for length in range(2, n + 1):  # 부분 문자열 길이
            for i in range(n - length + 1):  # 시작 인덱스
                j = i + length - 1  # 끝 인덱스
                if s[i] == s[j]:  # 양 끝 문자가 같으면
                    dp[i][j] = dp[i + 1][j - 1] + 2
                else:  # 다르면
                    dp[i][j] = max(dp[i + 1][j], dp[i][j - 1])

        #return dp[0][n - 1]  # 전체 문자열의 최장 회문 부분 수열 길이 반환
        return dp[0][-1]  # 전체 문자열의 최장 회문 부분 수열 길이 반환

 

'LeetCode > DP심화' 카테고리의 다른 글

115. Distinct Subsequences ★★★  (2) 2025.01.04
712. Minimum ASCII Delete Sum for Two Strings ★★  (0) 2025.01.04
931. Minimum Falling Path Sum  (0) 2025.01.03
740. Delete and Earn  (0) 2025.01.02
509. Fibonacci Number  (0) 2025.01.02