VOOZH about

URL: https://www.geeksforgeeks.org/dsa/construction-of-longest-increasing-subsequence-using-dynamic-programming/

⇱ Printing Longest Increasing Subsequence (LIS) - GeeksforGeeks


  • Courses
  • Tutorials
  • Interview Prep

Printing Longest Increasing Subsequence (LIS)

Last Updated : 23 Jul, 2025

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 order

Input:  [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.

[Naive Approach]: Dynamic programming with Subsequence Storage - O(n^3) Time and O(n^2) Space

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] where prev < i) to find the longest subsequence that can be extended by arr[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

  • Create an array of arrays L where L[i] stores the longest increasing subsequence (LIS) ending at index i.
  • Initialize L[0] with the first element (arr[0]), as it’s the only subsequence possible at this point.
  • For each element arr[i] (starting from i=1 to n-1):
    • Check all previous indices prev (from 0 to i-1).
    • If 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).
    • Append arr[i] to L[i] (since arr[i] is the endpoint of this subsequence).
  • After processing all elements, iterate through L[0] to L[n-1] to find the longest subsequence in L, which is the LIS.

Output
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

[Better Approach]: Dynamic programming with Single 1D Extra Array - O(n^2) Time and O(n) Space

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.

  1. 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[].
  2. After filling seq[] and dp[], we find the index of the largest element in dp.
  3. Now using the index found in step 2 and seq[], we construct the result sequence.

Output
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.

[Expected Approach] Using DP with Binary Search - O(n*log(n)) Time and O(n) Space

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 like lower_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

  • Create an empty list dp to store pairs (-value, index).
  • Create a map prv to remember the previous index for each element.
  • Iterate from the last element to the first.
  • For each element, take its negative (ve = -arr[ix]) to handle increasing order via decreasing values.
  • Use binary search (lower_bound) to find the right position in dp.
  • If the position is at the end of dp, append the element and set its previous index.
  • Else, update dp[i] with the current value and set its previous link.
  • Start from the last element's index (from dp.back().second).
  • Follow previous pointers (prv[cur]) to collect elements into the result list.
  • Since we built the sequence backward, finally reverse the ret array to get the LIS in correct order.

Output
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.


Comment