알고리즘

[백준] 9251 LCS Python

아람2 2024. 9. 30. 14:05
반응형

1. 문제

두 수열이 주어졌을 때, 모두의 부분 수열이 되는 수열 중 가장 긴 것을 찾는 문제이다
예를 들어, ACAYKP 와 CAPCAK 의 LCS 는 ACAK 가 된다 

2. 요구사항

시간 제한 0.1초

메모리 제한 256MB 

3. 알고리즘 분류

* 다이나믹 프로그래밍

* 문자열 

4. 접근 방식 

ㄷㅎ님이 기깔나게 설명해 주신 LCS 알고리즘을 이용하여 풀었다 LCS 알고리즘 정리한 내용 

문제에서 문자열 A "ACATKP" 과 문자열 B "CAPCAK" 을 비교한 내용을 표로 정리하면 아래와 같다 

  0 A C A Y K P
0 0 0 0 0 0 0 0
C 0 0 1 1 1 1 1
A 0 1 1 2 2 2 2
P 0 1 1 2 2 2 3
C 0 1 2 2 2 2 3
A 0 1 2 3 3 3 3
K 0 1 2 3 3 4 4

 

0 인 행렬을 추가한 이유는 비교 과정에서 대각선 +1 과 왼쪽/ 위쪽 비교를 위해서 추가했고,

그렇기 때문에 for 문의 범위를 m/ n+1 까지로 설정하여 첫번째 행과 열이 0으로 초기화된 상태에서 비교가 가능하다 

X 처리한 부분이 대각선 +1 된 부분이다 

5. 전체 코드

첫번째 시도 

# LCS Longest Common Subsequence 최장 공통 부분 수열 
# 두 수열이 주어졌을 때, 모두의 부분 수열이 되는 수열 중 가장 긴 것을 찾는 문제 

def lcs(A, B):
    n = len(A) # 첫번째 배열 길이 구하기
    m = len(B) # 두번째 배열 길이 구하기 

    # 2차원 DP 배열 초기화 (m+1 x n+1 크기)
    dp = [[0] * (n+1) for _ in range(m+1)]

	# DP 배열 채우기 
    for i in range(m+1):
        for j in range(n+1):
            if i == 0 or j == 0: # 마진 설정
                dp[i][j] = 0
            # dp 는 인덱스 1부터, A B 는 인덱스 0부터 시작하기 때문에
            # A[i-1] 와 B[j-1] 를 비교하여 dp[i][j] 값을 결정 
            elif A[i-1] == B[j-1]: # 두 문자가 같을 경우 
                dp[i][j] = dp[i-1][j-1] + 1
            else: # 두 문자가 다를 경우 
                 dp[i][j] = max(dp[i-1][j], dp[i][j-1])

    return dp[m][n] # 최장 공통 부분 수열 길이 반환 

if __name__ == '__main__':
    A = input()
    B = input()
    
    print(lcs(A, B)) # LCS 길이 출력

 

dp 배열은 인덱스 1부터, 문자열 A 와 B 배열은 인덱스 0부터 시작하기 때문에

A[i-1] 와 B[j-1] 를 비교하여 dp[i][j] 값을 결정한다, 그런데 인덱스 에러가 떴다

 

두번째 시도 

# LCS Longest Common Subsequence 최장 공통 부분 수열 
# 두 수열이 주어졌을 때, 모두의 부분 수열이 되는 수열 중 가장 긴 것을 찾는 문제 

def lcs(A, B):
    n = len(A) # 첫번째 배열 길이 구하기
    m = len(B) # 두번째 배열 길이 구하기 

    # 2차원 DP 배열 초기화 (m+1 x n+1 크기)
    dp = [[0] * (n+1) for _ in range(m+1)]

	# DP 배열 채우기 
    for i in range(1, m+1):
        for j in range(1, n+1):
            # 이미 모든 dp 배열을 0 으로 초기화했으므로
            # 인덱스 에러를 방지하기 위해 i, j == 0 고려하지 않음 
            # if i == 0 or j == 0: # 마진 설정
            #     dp[i][j] = 0 
            
            # dp 는 인덱스 1부터, A B 는 인덱스 0부터 시작하기 때문에
            # A[i-1] 와 B[j-1] 를 비교하여 dp[i][j] 값을 결정 
            if A[i-1] == B[j-1]: # 두 문자가 같을 경우 
                dp[i][j] = dp[i-1][j-1] + 1
            else: # 두 문자가 다를 경우 
                 dp[i][j] = max(dp[i-1][j], dp[i][j-1])

    return dp[m][n] # 최장 공통 부분 수열 길이 반환 

if __name__ == '__main__':
    A = input().strip()
    B = input().strip()
    
    print(lcs(A, B)) # LCS 길이 출력

 

이미 모든 dp 배열을 선언하면서 0으로 초기화했기 때문에 인덱스 에러를 방지하기 위해 

i, j == 0 조건을 없애고 A, B 입력 받을 때도 strip() 을 써줬다 그래도 인덱스 에러가 났다ㅡ.ㅡ

 

세번째 시도 

# LCS Longest Common Subsequence 최장 공통 부분 수열 
# 두 수열이 주어졌을 때, 모두의 부분 수열이 되는 수열 중 가장 긴 것을 찾는 문제 

def lcs(A, B):
    m = len(A) # 첫번째 배열 길이 구하기
    n = len(B) # 두번째 배열 길이 구하기 

    # 빈 문자열이 있는 경우 0 리턴 
    if n == 0 or m == 0:
            return 0
    
    # 2차원 DP 배열 초기화 (m+1 x n+1 크기)
    dp = [[0] * (n+1) for _ in range(m+1)]

	# DP 배열 채우기 
    for i in range(1, m+1):
        for j in range(1, n+1):
            # 이미 모든 dp 배열을 0 으로 초기화했으므로
            # 인덱스 에러를 방지하기 위해 i, j == 0 고려하지 않음 
            # if i == 0 or j == 0: # 마진 설정
            #     dp[i][j] = 0 
            
            # dp 는 인덱스 1부터, A B 는 인덱스 0부터 시작하기 때문에
            # A[i-1] 와 B[j-1] 를 비교하여 dp[i][j] 값을 결정 
            if A[i-1] == B[j-1]: # 두 문자가 같을 경우 
                dp[i][j] = dp[i-1][j-1] + 1
            else: # 두 문자가 다를 경우 
                dp[i][j] = max(dp[i-1][j], dp[i][j-1])

    return dp[m][n] # 최장 공통 부분 수열 길이 반환 

if __name__ == '__main__':
    A = input().strip()
    B = input().strip()
    
    print(lcs(A, B)) # LCS 길이 출력

 

사실 3번 더 인덱스 에러가 났는데,..... 코드를 다시 차근차근 읽어 보니

A 의 길이를 n, B 의 길이를 m 으로 할당하고 있었다 그래서 다시 바꿔주니 잘 동작했다

이 문제에 시간을 너무 많이 써버렸다, 정신 차리자!

# LCS Longest Common Subsequence 최장 공통 부분 수열 
# 두 수열이 주어졌을 때, 모두의 부분 수열이 되는 수열 중 가장 긴 것을 찾는 문제 

def lcs(A, B):
    m = len(A) # 첫번째 배열 길이 구하기
    n = len(B) # 두번째 배열 길이 구하기 

    # 빈 문자열이 있는 경우 0 리턴 
    if n == 0 or m == 0:
            return 0
    
    # 2차원 DP 배열 초기화 (m+1 x n+1 크기)
    dp = [[0] * (n+1) for _ in range(m+1)]

	# DP 배열 채우기 
    for i in range(1, m+1):
        for j in range(1, n+1):
            # 이미 모든 dp 배열을 0 으로 초기화했으므로
            # 인덱스 에러를 방지하기 위해 i, j == 0 고려하지 않음 
            # if i == 0 or j == 0: # 마진 설정
            #     dp[i][j] = 0 
            
            # dp 는 인덱스 1부터, A B 는 인덱스 0부터 시작하기 때문에
            # A[i-1] 와 B[j-1] 를 비교하여 dp[i][j] 값을 결정 
            if A[i-1] == B[j-1]: # 두 문자가 같을 경우 
                dp[i][j] = dp[i-1][j-1] + 1
            else: # 두 문자가 다를 경우 
                dp[i][j] = max(dp[i-1][j], dp[i][j-1])

    return dp[m][n] # 최장 공통 부분 수열 길이 반환 

if __name__ == '__main__':
    A = input().strip()
    B = input().strip()
    
    print(lcs(A, B)) # LCS 길이 출력

반응형