티스토리 뷰

728x90

LIS(Longest increasing Subsequence)란, 가장 긴 증가하는 부분 수열이다.

 

예를 들어,

[6, 2, 5, 1, 7, 4, 8, 3] 이라는 배열이 있을 경우, LIS는 [2, 5, 7, 8]이 된다.

증가하는 부분 수열 중 가장 긴 것이기 때문.

 

 

LIS를 풀기 위한 가장 일반적인 방법은 DP를 이용하는 것이다.

dp = [1]*n
for i in range(n): 
	for j in range(i): #첫번째 요소부터 i번째까지 위와 비교
    	if arr[i] > arr[j]: #뒤에 있는 요소(arr[i])가 크면
        	dp[i] = max(dp[i], dp[k]+1)
        	

위 알고리즘의 시간복잡도는 O(n^2)을 갖게 됩니다.

 

시간복잡도를 개선하기 위해서는 이분탐색을 활용합니다.

주어진 배열의 인덱스를 하나씩 살펴보면서 그 숫자가 들어갈 위치를 이분탐색으로 탐색해서 넣습니다.

이분탐색은 일반적으로 시간복잡도가 O(log n)으로 알려져있으므로,

이분탐색을 활용한 LIS의 길이 구하기의 시간복잡도는 O(nlog n)으로 개선시킬 수 있습니다.

memorization = [0]
arr = [0] + 원래 배열

for case in cases:
	if memoization[-1] < case:
    	memoization.append(case)
    else:
    	left = 0
        right = len(memoization)
        
        while left<right:
        	mid = (left+right)//2
            
            if memoization[mid] < case:
            	left = mid + 1
            else:
            	right = mid
     	memoization[right] = case

[100, 50, 70, 90, 75, 87, 105, 78, 110, 60] 이 배열의 LIS를 구한다고 가정해보면,

LIS는 [50, 70, 75, 87, 105, 110] 로, 길이는 6이 된다.

아래와 같은 순서로 진행된다.

[0] 
[0, 100]
[0, 50]
[0, 50, 70]
[0, 50, 70, 90]
[0, 50, 70, 75]
[0, 50, 70, 75, 87]
[0, 50, 70, 75, 87, 105]
[0, 50, 70, 75, 78, 105]

[0, 50, 70, 75, 78, 105, 110]
[0, 50, 60, 75, 78, 105, 110]

0을 뺀 나머지의 길이를 구하면 LIS의 길이가 된다.

 

이분탐색으로 구한 LIS와 실제 LIS의 값들이 다른 것을 주의깊게 봐야한다.

수열상에서 뒤에 있던 원소가 먼저 들어온 원소보다 lower_bound로 탐색된 최적의 위치가 앞에 있을 수도 있기 때문입니다.

따라서 배열에 들어있는 값들은 LIS를 이루는 요소와 무관하다는 것이다.

반응형