새소식

Computer Science/코딩테스트 문제 풀이

[백준][파이썬][LCS][이분탐색] 13711번: LCS 4

  • -

 

문제집: https://www.acmicpc.net/workbook/by/BaaaaaaaaaaarkingDog

 

문제: https://www.acmicpc.net/problem/13711

 

이전 문제:

LCS https://hyundoil.tistory.com/357

LCS2 https://hyundoil.tistory.com/369

LCS3 https://hyundoil.tistory.com/370

 

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

예를 들어, [1, 2, 3]과 [1, 3, 2]의 LCS는 [1, 2] 또는 [1, 3] 이다. 

1부터 N까지 정수가 모두 한 번씩 등장하는 두 수열 A와 B가 주어졌을 때, 두 수열의 LCS 크기를 구하는 프로그램을 작성하시오.

첫째 줄에 두 수열의 크기 N (1 ≤ N ≤ 100,000)이 주어진다.

둘째 줄에는 수열 A에 들어있는 수가, 셋째 줄에는 수열 B에 들어있는 수가 주어진다.

두 수열의 LCS의 크기를 출력한다.

2 1 2 2 1
1
3 1 2 3 1 3 2
2

 

 

해당 문제를 보게되면 기존에 LCS처럼 DP로 풀면될 것 같다. 

그러나 DP로 풀면 메모리 문제가 발생한다. 

 

일단 문제를 자세히 살펴보면 ' 1부터 N까지 정수가 모두 한 번씩 등장하는 두 수열 A와 B가 주어졌을 때'

라고 한다. 

 

메모리 문제를 해결하기 위해 이분탐색을 해야하고 이분탐색이니 당연히 DP보다 속도나 메모리에 대해서는 더 이점일 것이다. 

그럼 다른 LCS문제도 DP가 아닌 이분탐색으로 풀면 안되나? 

안된다고 한다. 그 이유를  

 

ChatGPT에 물어보니 

수열에서의 LCS 문제는 숫자로 구성되어 있어, 숫자의 상대적인 크기가 중요하므로 이분 탐색이 가능하다고 한다. 

숫자 집합은 그 값 자체로 크기를 알 수 있으니 이분탐색으로 쉽게 위치를 찾을 수 있다. 

반면 문자열에서의 LCS 문제는 문자열에 순서가 없으니 이분탐색을 할 수 없다고 한다. 

 

import bisect def solution(N, A, B): # A의 각 숫자의 인덱스를 기록하는 딕셔너리 pos_in_A = {num: idx for idx, num in enumerate(A)} # B의 숫자를 A에서의 인덱스로 변환한 리스트 indices_in_A = [pos_in_A[num] for num in B] # 가장 긴 증가하는 부분 수열(LIS)을 구하기 위한 리스트 lis = [] # B에 대한 A의 인덱스 리스트를 순회하면서 LIS를 구함 for idx in indices_in_A: # LIS 배열에서 idx가 들어갈 위치를 찾음 pos = bisect.bisect_left(lis, idx) if pos < len(lis): lis[pos] = idx # 갱신 else: lis.append(idx) # 새로 추가 # 최장 공통 부분 수열의 길이 출력 print(len(lis)) if __name__ == "__main__": N = int(input().strip()) # 수열 크기 A = list(map(int, input().split())) # 첫 번째 수열 B = list(map(int, input().split())) # 두 번째 수열 solution(N, A, B)

 

 하... 어렵다.

 

 

만약 LCS 값도 출력하고 싶으면 

 

import sys sys.stdin=open('input.txt', 'r') import bisect def solution(N, A, B): posinA = {num: idx for idx, num in enumerate(A)} indicesinA = [posinA[num] for num in B] lis = [] result = [] for i in indicesinA: pos = bisect.bisect_left(lis, i) if pos < len(lis): lis[pos] = i result[pos] = A[i] else: lis.append(i) result.append(A[i]) print(len(lis)) print(result) if __name__ == "__main__": N = int(input()) nums1 = list(map(int, input().split())) nums2 = list(map(int, input().split())) solution(N, nums1, nums2)

 

 

 

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.