![]() |
VOOZH | about |
Given a sequence of numbers, the task is to find and print the Longest Increasing Subsequence (LIS), the longest subsequence where each element is strictly greater than the previous one. If multiple LIS of the same maximum length exist, we must select the one that appears first based on the lexicographical order of their indices (i.e., the earliest combination of positions from the original sequence). The goal is to ensure both maximum length and the earliest possible subsequence.
Examples:
Input: [10, 20, 3, 40]
Output : 10 20 40
Explanation:[10, 20, 40]is the longest subsequence where each number is greater than the previous one, maintaining the original orderInput: [10, 22, 9, 33, 21, 50, 41, 60, 80]
Output : 10 22 33 50 60 80
Explanation: There are multiple longest Increasing subsequence of length 6, examples [10, 22, 33, 50, 60, 80] and [10 22 33 41 60 80]. The first one has lexicographic smallest order.
Table of Content
We extend the of bottom-up DP solution of LIS problem. Please remember, for every element
arr[i], it iterates over all previous elements (arr[prev]whereprev < i) to find the longest subsequence that can be extended byarr[i]. To find elements of LIS, we build an array of arrays, L such that L[i] stores LIS of arr that ends with arr[i]. For example, for array [3, 2, 6, 4, 5, 1].L[0] = [3]
L[1] = [2]
L[2] = [2, 6]
L[3] = [2, 4]
L[4] = [2, 4, 5]
L[5] = [1]The final result is the longest subsequence among all
L[i].
Step by step Approach
L where L[i] stores the longest increasing subsequence (LIS) ending at index i.L[0] with the first element (arr[0]), as itβs the only subsequence possible at this point.arr[i] (starting from i=1 to n-1):prev (from 0 to i-1).arr[i] > arr[prev] (valid to extend the subsequence) and the subsequence L[prev] is longer than the current best for L[i]: Update L[i] by copying L[prev] (this extends the subsequence ending at prev).arr[i] to L[i] (since arr[i] is the endpoint of this subsequence).L[0] to L[n-1] to find the longest subsequence in L, which is the LIS.10 20 40
Time Complexity: O(n3), n2 for two nested loops and n for copying another vector in a vector (e.g. : L[i] = L[prev]) contributes O(n) also.
Space Complexity: O(n2), 2d vector to store our LIS
In the previous solution, we use an array of arrays to store all subsequences ending with every index. The idea here is to store only indexes of only the previous item in the answer LIS.
- We use an array seq[] to store indexes of previous items in LIS and used to construct the resultant sequence. Along with constructing dp[] array, we fill indexes in seq[].
- After filling seq[] and dp[], we find the index of the largest element in dp.
- Now using the index found in step 2 and seq[], we construct the result sequence.
10 20 40
Time Complexity: O(n2), We have two nested loops: one to fill the dp[] array and another to backtrack using the seq[]array.
Space Complexity: O(n), We use two arrays, dp[]and seq[], each of size n.
The idea is based on LIS using binary search to improve efficiency. We maintain a list (
dp) representing the minimum possible last value for increasing subsequences of different lengths. We process elements backward and use negative values to flip the problem into a decreasing order, making it easier to find the correct position using binary search function likelower_bound() in C++. Tracking previous indices allows us to reconstruct the actual sequence after processing. This method ensures both maximum length and earliest lexicographical order efficiently.
Step by Step Approach
dp to store pairs (-value, index).prv to remember the previous index for each element.ve = -arr[ix]) to handle increasing order via decreasing values.lower_bound) to find the right position in dp.dp, append the element and set its previous index.dp[i] with the current value and set its previous link.dp.back().second).prv[cur]) to collect elements into the result list.ret array to get the LIS in correct order.10 20 40
Time Complexity: O(N log N) ,for each of the N elements we perform a binary search (using lower_bound) in the dp array, which takes O(log N) time. Thus, processing all elements gives an overall complexity of O(N log N).
Auxiliary space: O(N), we use extra space for the dp list and the prv map, both of which at most store one entry per element in the input array.