
"오름차순 수열의 최대 길이"를 찾는 문제라고 생각하고 풀었다.
첫번째 시도
class Solution:
def lengthOfLIS(self, nums: List[int]) -> int:
N = len(nums)
dp = [1]*N # dp[i] = index i까지의 최댓값
for i in range(1, N):
if nums[i] < nums[i-1]:
dp[i] = dp[i-1] # index i의 값이 i-1의 값보다 작다면 증가하는 수열에 포함되지 않음
elif nums[i] > nums[i-1]:
dp[i] = dp[i-1] + 1 # 증가하는 수열에 포함
return max(dp)
처음에는 DP(다이나믹 프로그래밍)을 활용해서
dp[i] = dp[i-1]로 최대 수열 길이를 유지한다.dp[i] = dp[i-1]+1 최대 수열 길이에 +1을 한다.이러한 사고 흐름으로 코드를 구현했다.
그러나 24번째 tastcase에서 실패했다.
실패 테스트 케이스
input: nums = [4,10,4,3,8,9]
output: Exptected: 3
내가 구현한 코드의 흐름으로 실패 테스트 케이스를 실행하면
dp = [1,2,2,2,3,4] -> 즉, Output으로 4가 나온다.
현재 코드의 문제점
현재 방식은 '인접한 두 원소'만 비교하고 있어서, 전체 증가 수열 중에서 가장 긴 걸 찾지 못한다.
for i in range(1, N):
if nums[i] < nums[i-1]:
dp[i] = dp[i-1]
elif nums[i] > nums[i-1]:
dp[i] = dp[i-1] + 1
해당 루프는 i와 i-1만 비교하고 있다.
문제를 해결하려면 nums[0] ~ nums[i-1]까지 모든 이전 원소들 중에서 nums[i]보다 작은 값들을 고려해야 한다.
수정 코드
class Solution:
def lengthOfLIS(self, nums: List[int]) -> int:
N = len(nums)
dp = [1]*N # dp[i] = index i까지의 최댓값
for i in range(1, N):
for j in range(i):
if nums[j] < nums[i]:
dp[i] = max(dp[i], dp[j]+1)
return max(dp)
코드는 통과했다. 하지만 현재 코드는 O(n^2)의 시간 복잡도를 갖는다.
O(nlogn) 코드
class Solution:
def lengthOfLIS(self, nums: List[int]) -> int:
dp = []
def binary_search(target): # 이진 탐색
left, right = 0, len(dp)-1
while left <= right:
mid = (left+right)//2
if dp[mid] < target:
left = mid + 1
else:
right = mid - 1
return left
for num in nums:
idx = binary_search(num)
if idx == len(dp):
dp.append(num)
else:
dp[idx] = num
return len(dp)
LIS의 길이=정답이다.요약: nums 순회 → 이진 탐색으로 위치 찾기 → dp에 삽입 or 대체 → dp 길이 = 정답
LIS: Longest increasing Subsequence, 최장 증가 부분 수열