Longest Increasing Subsequence in C++
Last Updated :
24 Jul, 2024
In this article, we will learn the concept of the Longest Increasing Subsequence (LIS) in a given sequence using C++ language. The LIS problem is about finding the longest subsequence of a sequence in which the elements are in sorted order, from lowest to highest, and not necessarily contiguous.
Example:
Input:
int arr[] = { 3, 10, 2, 1, 20 };
Output:
Length of the longest increasing subsequence is: 3
Longest Increasing Subsequence in C++Below, we have discussed the multiple approaches to find the Longest Increasing Subsequence from a given array in C++.
In this approach, we will explore all the possible subsequences to find the longest increasing one by comparing each element with previous elements to check if it can be included in the increasing sequence or not.
Approach:
- Define a function lisRecursive that takes the array arr and its length n.
- Base Case: If the length is 1, return 1.
- For each element, recursively compute the LIS ending at each index.
- Return the maximum length found.
Below is the implementation of the above recursive approach in C++:
C++
// C++ program to find the length of the longest increasing
// subsequence (LIS) using a recursive approach
#include <iostream>
using namespace std;
// Function to return the maximum of two integers
int max(int a, int b) { return (a > b) ? a : b; }
// Recursive function to calculate LIS ending at arr[n-1]
int lisRecursive(int arr[], int n, int* max_ref)
{
// Base case: single element is an increasing
// subsequence of length 1
if (n == 1)
return 1;
// Initialize the max ending length
int res, max_ending_here = 1;
// Loop through all elements before arr[n-1]
for (int i = 1; i < n; i++) {
// Recursively calculate LIS ending at arr[i-1]
res = lisRecursive(arr, i, max_ref);
// Update max_ending_here if arr[i-1] is less than
// arr[n-1] and results in a longer subsequence
if (arr[i - 1] < arr[n - 1]
&& res + 1 > max_ending_here)
max_ending_here = res + 1;
}
// Update the overall maximum length of LIS found so far
if (*max_ref < max_ending_here)
*max_ref = max_ending_here;
// Return length of LIS ending at arr[n-1]
return max_ending_here;
}
// Function to calculate the length of the longest
// increasing subsequence
int lis(int arr[], int n)
{
// Initialize maximum length of LIS
int max = 1;
// Call recursive function
lisRecursive(arr, n, &max);
// Return the maximum length of LIS
return max;
}
int main()
{
// Initialize array and find its size
int arr[] = { 3, 10, 2, 1, 20 };
int n = sizeof(arr) / sizeof(arr[0]);
// Print the length of the longest increasing
// subsequence
cout << "Length of LIS is " << lis(arr, n) << endl;
return 0;
}
Time Complexity: O(2^n), due to overlapping subproblems.
Auxiliary Space: O(1), excluding internal stack space.
We can use the memoization technique to improve the recursive approach by storing the results of subproblems to avoid redundant calculations.
Approach:
- Create a memoization array memo to store the LIS for each index.
- Define a function lisMemoization that takes arr, n, and memo.
- Base Case: If the length is 1, return 1.
- If the result is already computed, return it.
- Recursively compute and store results for the remaining elements.
- Return the computed result from the memoization array.
Below is the implementation of the above memoization technique in C++:
C++
// C++ program to find the length of the longest increasing
// subsequence (LIS) using memoization
#include <cstring>
#include <iostream>
#include <vector>
using namespace std;
// Function to return the maximum of two integers
int max(int a, int b) { return (a > b) ? a : b; }
// Recursive function to calculate LIS ending at arr[n-1]
// using memoization
int lisMemoization(int arr[], int n, int* memo)
{
// Check if result is already computed
if (memo[n] != -1)
return memo[n];
// Initialize the max ending length
int res, max_ending_here = 1;
// Loop through all elements before arr[n-1]
for (int i = 1; i < n; i++) {
// Recursively calculate LIS ending at arr[i-1] and
// update max_ending_here
res = lisMemoization(arr, i, memo);
if (arr[i - 1] < arr[n - 1]
&& res + 1 > max_ending_here)
max_ending_here = res + 1;
}
// Store the computed value in memo array and return it
return memo[n] = max_ending_here;
}
// Function to calculate the length of the longest
// increasing subsequence
int lis(int arr[], int n)
{
// Initialize memoization vector with -1
vector<int> memo(n + 1, -1);
// Initialize maximum length of LIS
int maxLength = 1;
// Calculate LIS for each element and update maxLength
for (int i = 1; i <= n; i++)
maxLength = max(
maxLength, lisMemoization(arr, i, memo.data()));
// Return the maximum length of LIS
return maxLength;
}
int main()
{
// Initialize array and find its size
int arr[] = { 3, 10, 2, 1, 20 };
int n = sizeof(arr) / sizeof(arr[0]);
// Print the length of the longest increasing
// subsequence
cout << "Length of LIS is " << lis(arr, n) << endl;
return 0;
}
Time Complexity: O(n^2), due to nested loop.
Auxiliary Space: O(n), for memoization array.
In this method, we will use the concept of dynamic programming to iteratively computes the LIS by storing intermediate results, making the process more efficient.
Approach:
- Create an array lis of size n and initialize all values to 1.
- For each element, find the longest subsequence ending at that element.
- Return the maximum value in the lis array, which will be the length of the LIS.
Below is the implementation of the above dynamic programming approach in C++:
C++
// C++ program to find the length of the longest increasing
// subsequence (LIS) using tabulation
#include <iostream>
#include <vector>
using namespace std;
// Function to return the maximum of two integers
int max(int a, int b) { return (a > b) ? a : b; }
// Function to calculate the length of the longest
// increasing subsequence using tabulation
int lisTabulation(int arr[], int n)
{
// Initialize a vector to store the LIS values for each
// element
vector<int> lis(n, 1);
// Loop through each element starting from the second
for (int i = 1; i < n; i++) {
// For each element, compare it with all previous
// elements
for (int j = 0; j < i; j++) {
// If the current element is greater and the LIS
// value can be increased
if (arr[i] > arr[j] && lis[i] < lis[j] + 1)
lis[i] = lis[j] + 1;
}
}
// Initialize the variable to store the maximum length
// of LIS
int maxLength = 0;
// Find the maximum value in the lis vector
for (int i = 0; i < n; i++)
maxLength = max(maxLength, lis[i]);
return maxLength; // Return the maximum length of LIS
}
int main()
{
// Initialize array and find its size
int arr[] = { 3, 10, 2, 1, 20 };
int n = sizeof(arr) / sizeof(arr[0]);
// Print the length of the longest increasing
// subsequence
cout << "Length of LIS is " << lisTabulation(arr, n)
<< endl;
return 0;
}
Time Complexity: O(n^2)
Auxiliary Space: O(n), for storing LIS values at each index.
We can also, use the binary search to improve time complexity by efficiently finding the position where the current element should be placed in the subsequence.
Approach:
- Create an array tail to store the smallest ending element of all increasing subsequences of length i+1.
- Use binary search to find the position of the current element in tail.
- If the element is larger than the largest element in tail, extend tail. Otherwise, replace the existing element in tail.
- Return the length of the tail array, which is the length of the LIS.
Below is the implementation of the above binary search approach in C++:
C++
// C++ program to find the length of the longest increasing
// subsequence (LIS) using binary search
#include <iostream>
#include <vector>
using namespace std;
// Function to perform binary search on the tail array
int binarySearch(vector<int>& tail, int l, int r, int key)
{
// Loop until the search space is reduced to one element
while (r - l > 1) {
// Find the middle element
int m = l + (r - l) / 2;
// Update the search space based on the comparison
if (tail[m] >= key)
r = m;
else
l = m;
}
// Return the position to insert the key
return r;
}
// Function to calculate the length of the longest
// increasing subsequence using binary search
int lisBinarySearch(int arr[], int n)
{
// Check if the array is empty
if (n == 0)
return 0;
// Initialize the tail array to store the smallest
// ending element of each length subsequence
vector<int> tail(n);
// Initialize the length of the LIS
int length = 1;
// The first element is the initial subsequence
tail[0] = arr[0];
// Loop through the array elements
for (int i = 1; i < n; i++) {
// If the current element is smaller than the first
// element in tail
if (arr[i] < tail[0])
tail[0] = arr[i];
// If the current element is greater than the last
// element in tail
else if (arr[i] > tail[length - 1])
tail[length++] = arr[i];
// If the current element can replace an element in
// the middle
else
tail[binarySearch(tail, -1, length - 1, arr[i])]
= arr[i];
}
// Return the length of the LIS
return length;
}
int main()
{
// Initialize the array and find its size
int arr[] = { 3, 10, 2, 1, 20 };
int n = sizeof(arr) / sizeof(arr[0]);
// Print the length of the longest increasing
// subsequence
cout << "Length of LIS is " << lisBinarySearch(arr, n)
<< endl;
return 0;
}
Time Complexity: O(n log n)
Auxiliary Space: O(n), for storing the tail array.
Similar Reads
Longest Increasing Subsequence using BIT
Given an array arr, the task is to find the length of The Longest Increasing Sequence using Binary Indexed Tree (BIT) Examples: Input: arr = {6, 5, 1, 3, 2, 4, 8, 7} Output: 4 Explanation: The possible subsequences are {1 2 4 7}, {1 2 4 8}, {1 3 4 8}, {1 3 4 7} Now insert the elements one by one fro
13 min read
Longest Palindromic Subsequence in C++
In this article, we will learn how to find the Longest Palindromic Subsequence (LPS) of a sequence using C++ programming language. LPS is the longest subsequence of a given sequence that reads the same backward as forward. Example: Input:Sequence: "BBABCBCAB"Output:Length of LPS is 7Explanation: The
7 min read
Minimizing and Maximizing longest Increasing Subsequence (LIS)
Given a string S of length n-1, consisting of characters '<' and '>' only. The ith character is the comparison result between the ith element and the (i+1)th element of the sequence. If the ith character of the string is '<', then the ith element of the sequence is less than the (i+1)st ele
9 min read
Longest Palindromic Subsequence in Python
Longest Palindromic Subsequence (LPS) problem is about finding the longest subsequence of the given sequence which is a palindrome. In Python, the task of maximizing the number of words in a sentence can be solved by the methods of dynamic programming. The algorithm for finding the Longest Palindrom
4 min read
Longest strictly decreasing subsequence such that no two adjacent elements are coprime
Given an array arr[], find the length of the longest strictly decreasing subsequence such that no two adjacent elements are coprime. Examples: Input: N = 5, arr[]= {9, 6, 4, 3, 2}Output: 4Explanation: The longest strictly decreasing non-coprime subsequence is {9, 6, 4, 2} having length = 4. Input: N
9 min read
CSES solution-Repeating Substring
A repeating substring is a substring that occurs in two (or more) locations in the string. Your task is to find the longest repeating substring in a given string. Example: Input: s = "cabababc"Output: abab Input: s = "babababb"Output: babab Approach: The solution is based on two main concepts: Suffi
12 min read
Queries to find longest subsequence having no similar adjacent elements with updates
Given an array arr[] consisting of N integers and an array Queries[][] with each query of the form {x, y}. For each query, the task is to replace the element at index x ( 1-based indexing ) by y and find the length of the longest subsequence having no similar adjacent elements. Examples: Input: arr[
14 min read
Longest substring with K unique characters using Binary Search
Given a string str and an integer K, the task is to print the length of the longest possible substring that has exactly K unique characters. If there is more than one substring of the longest possible length, then print any one of them or print -1 if there is no such substring possible. Examples: In
10 min read
Lexicographically smallest subsequence possible by removing a character from given string
Given a string S of length N, the task is to find the lexicographically smallest subsequence of length (N - 1), i.e. by removing a single character from the given string. Examples: Input: S = "geeksforgeeks"Output: "eeksforgeeks"Explanation: Lexicographically smallest subsequence possible is "eeksfo
8 min read
Maximum size subset with given sum using Backtracking
Given an array arr[] consisting of N integers and an integer K, the task is to find the length of the longest subsequence with a sum equal to K.Examples: Input: arr[] = {-4, -2, -2, -1, 6}, K = 0 Output: 3 Explanation: The longest subsequence is of length 3 which is {-4, -2, 6} having sum 0.Input: a
9 min read