The Longest Bitonic Subsequence problem is to find the longest subsequence of a given sequence such that it is first increasing and then decreasing. A sequence, sorted in increasing order is considered Bitonic with the decreasing part as empty. Similarly, decreasing order sequence is considered Bitonic with the increasing part as empty. Examples:
Input: [1, 11, 2, 10, 4, 5, 2, 1]
Output: [1, 2, 10, 4, 2, 1] OR [1, 11, 10, 5, 2, 1]
OR [1, 2, 4, 5, 2, 1]
Input: [12, 11, 40, 5, 3, 1]
Output: [12, 11, 5, 3, 1] OR [12, 40, 5, 3, 1]
Input: [80, 60, 30, 40, 20, 10]
Output: [80, 60, 30, 20, 10] OR [80, 60, 40, 20, 10]
In previous post, we have discussed about Longest Bitonic Subsequence problem. However, the post only covered code related to finding maximum sum of increasing subsequence, but not to the construction of subsequence. In this post, we will discuss how to construct Longest Bitonic Subsequence itself. Let arr[0..n-1] be the input array. We define vector LIS such that LIS[i] is itself is a vector that stores Longest Increasing Subsequence of arr[0..i] that ends with arr[i]. Therefore for an index i, LIS[i] can be recursively written as –
LIS[0] = {arr[O]}
LIS[i] = {Max(LIS[j])} + arr[i] where j < i and arr[j] < arr[i]
= arr[i], if there is no such j
We also define a vector LDS such that LDS[i] is itself is a vector that stores Longest Decreasing Subsequence of arr[i..n] that starts with arr[i]. Therefore for an index i, LDS[i] can be recursively written as –
LDS[n] = {arr[n]}
LDS[i] = arr[i] + {Max(LDS[j])} where j > i and arr[j] < arr[i]
= arr[i], if there is no such j
For example, for array [1 11 2 10 4 5 2 1],
LIS[0]: 1
LIS[1]: 1 11
LIS[2]: 1 2
LIS[3]: 1 2 10
LIS[4]: 1 2 4
LIS[5]: 1 2 4 5
LIS[6]: 1 2
LIS[7]: 1
LDS[0]: 1
LDS[1]: 11 10 5 2 1
LDS[2]: 2 1
LDS[3]: 10 5 2 1
LDS[4]: 4 2 1
LDS[5]: 5 2 1
LDS[6]: 2 1
LDS[7]: 1
Therefore, Longest Bitonic Subsequence can be
LIS[1] + LDS[1] = [1 11 10 5 2 1] OR
LIS[3] + LDS[3] = [1 2 10 5 2 1] OR
LIS[5] + LDS[5] = [1 2 4 5 2 1]
Below is the implementation of above idea –
C++
#include <bits/stdc++.h>
using namespace std;
void print(vector< int >& arr, int size)
{
for ( int i = 0; i < size; i++)
cout << arr[i] << " " ;
}
void printLBS( int arr[], int n)
{
vector<vector< int >> LIS(n);
LIS[0].push_back(arr[0]);
for ( int i = 1; i < n; i++)
{
for ( int j = 0; j < i; j++)
{
if ((arr[j] < arr[i]) &&
(LIS[j].size() > LIS[i].size()))
LIS[i] = LIS[j];
}
LIS[i].push_back(arr[i]);
}
vector<vector< int >> LDS(n);
LDS[n - 1].push_back(arr[n - 1]);
for ( int i = n - 2; i >= 0; i--)
{
for ( int j = n - 1; j > i; j--)
{
if ((arr[j] < arr[i]) &&
(LDS[j].size() > LDS[i].size()))
LDS[i] = LDS[j];
}
LDS[i].push_back(arr[i]);
}
for ( int i = 0; i < n; i++)
reverse(LDS[i].begin(), LDS[i].end());
int max = 0;
int maxIndex = -1;
for ( int i = 0; i < n; i++)
{
if (LIS[i].size() + LDS[i].size() - 1 > max)
{
max = LIS[i].size() + LDS[i].size() - 1;
maxIndex = i;
}
}
print(LIS[maxIndex], LIS[maxIndex].size() - 1);
print(LDS[maxIndex], LDS[maxIndex].size());
}
int main()
{
int arr[] = { 1, 11, 2, 10, 4, 5, 2, 1 };
int n = sizeof (arr) / sizeof (arr[0]);
printLBS(arr, n);
return 0;
}
|
Java
import java.util.*;
class GFG
{
static void print(Vector<Integer> arr, int size)
{
for ( int i = 0 ; i < size; i++)
System.out.print(arr.elementAt(i) + " " );
}
static void printLBS( int [] arr, int n)
{
@SuppressWarnings ( "unchecked" )
Vector<Integer>[] LIS = new Vector[n];
for ( int i = 0 ; i < n; i++)
LIS[i] = new Vector<>();
LIS[ 0 ].add(arr[ 0 ]);
for ( int i = 1 ; i < n; i++)
{
for ( int j = 0 ; j < i; j++)
{
if ((arr[i] > arr[j]) &&
LIS[j].size() > LIS[i].size())
{
for ( int k : LIS[j])
if (!LIS[i].contains(k))
LIS[i].add(k);
}
}
LIS[i].add(arr[i]);
}
@SuppressWarnings ( "unchecked" )
Vector<Integer>[] LDS = new Vector[n];
for ( int i = 0 ; i < n; i++)
LDS[i] = new Vector<>();
LDS[n - 1 ].add(arr[n - 1 ]);
for ( int i = n - 2 ; i >= 0 ; i--)
{
for ( int j = n - 1 ; j > i; j--)
{
if (arr[j] < arr[i] &&
LDS[j].size() > LDS[i].size())
for ( int k : LDS[j])
if (!LDS[i].contains(k))
LDS[i].add(k);
}
LDS[i].add(arr[i]);
}
for ( int i = 0 ; i < n; i++)
Collections.reverse(LDS[i]);
int max = 0 ;
int maxIndex = - 1 ;
for ( int i = 0 ; i < n; i++)
{
if (LIS[i].size() + LDS[i].size() - 1 > max)
{
max = LIS[i].size() + LDS[i].size() - 1 ;
maxIndex = i;
}
}
print(LIS[maxIndex], LIS[maxIndex].size() - 1 );
print(LDS[maxIndex], LDS[maxIndex].size());
}
public static void main(String[] args)
{
int [] arr = { 1 , 11 , 2 , 10 , 4 , 5 , 2 , 1 };
int n = arr.length;
printLBS(arr, n);
}
}
|
Python3
def _print(arr: list , size: int ):
for i in range (size):
print (arr[i], end = " " )
def printLBS(arr: list , n: int ):
LIS = [ 0 ] * n
for i in range (n):
LIS[i] = []
LIS[ 0 ].append(arr[ 0 ])
for i in range ( 1 , n):
for j in range (i):
if ((arr[j] < arr[i]) and ( len (LIS[j]) > len (LIS[i]))):
LIS[i] = LIS[j].copy()
LIS[i].append(arr[i])
LDS = [ 0 ] * n
for i in range (n):
LDS[i] = []
LDS[n - 1 ].append(arr[n - 1 ])
for i in range (n - 2 , - 1 , - 1 ):
for j in range (n - 1 , i, - 1 ):
if ((arr[j] < arr[i]) and ( len (LDS[j]) > len (LDS[i]))):
LDS[i] = LDS[j].copy()
LDS[i].append(arr[i])
for i in range (n):
LDS[i] = list ( reversed (LDS[i]))
max = 0
maxIndex = - 1
for i in range (n):
if ( len (LIS[i]) + len (LDS[i]) - 1 > max ):
max = len (LIS[i]) + len (LDS[i]) - 1
maxIndex = i
_print(LIS[maxIndex], len (LIS[maxIndex]) - 1 )
_print(LDS[maxIndex], len (LDS[maxIndex]))
if __name__ = = "__main__" :
arr = [ 1 , 11 , 2 , 10 , 4 , 5 , 2 , 1 ]
n = len (arr)
printLBS(arr, n)
|
C#
using System;
using System.Linq;
using System.Collections.Generic;
class GFG
{
static void print(List< int > arr, int size)
{
for ( int i = 0; i < size; i++)
Console.Write(arr[i] + " " );
}
static void printLBS( int [] arr, int n)
{
List< int >[] LIS = new List< int >[n];
for ( int i = 0; i < n; i++)
LIS[i] = new List< int >();
LIS[0].Add(arr[0]);
for ( int i = 1; i < n; i++)
{
for ( int j = 0; j < i; j++)
{
if ((arr[i] > arr[j]) &&
LIS[j].Count > LIS[i].Count)
{
foreach ( int k in LIS[j])
if (!LIS[i].Contains(k))
LIS[i].Add(k);
}
}
LIS[i].Add(arr[i]);
}
List< int >[] LDS = new List< int >[n];
for ( int i = 0; i < n; i++)
LDS[i] = new List< int >();
LDS[n - 1].Add(arr[n - 1]);
for ( int i = n - 2; i >= 0; i--)
{
for ( int j = n - 1; j > i; j--)
{
if (arr[j] < arr[i] &&
LDS[j].Count > LDS[i].Count)
foreach ( int k in LDS[j])
if (!LDS[i].Contains(k))
LDS[i].Add(k);
}
LDS[i].Add(arr[i]);
}
for ( int i = 0; i < n; i++)
LDS[i].Reverse();
int max = 0;
int maxIndex = -1;
for ( int i = 0; i < n; i++)
{
if (LIS[i].Count + LDS[i].Count - 1 > max)
{
max = LIS[i].Count + LDS[i].Count - 1;
maxIndex = i;
}
}
print(LIS[maxIndex], LIS[maxIndex].Count - 1);
print(LDS[maxIndex], LDS[maxIndex].Count);
}
public static void Main(String[] args)
{
int [] arr = { 1, 11, 2, 10, 4, 5, 2, 1 };
int n = arr.Length;
printLBS(arr, n);
}
}
|
Javascript
function _print(arr, size) {
for (let i = 0; i<size; i++) {
process.stdout.write(arr[i]+ ' ' );
}
}
function printLBS(arr, n) {
let LIS = new Array(n);
for (let i = 0; i < n; i++) {
LIS[i] = [];
}
LIS[0].push(arr[0]);
for (let i = 1; i < n; i++) {
for (let j = 0; j < i; j++) {
if (arr[j] < arr[i] && LIS[j].length > LIS[i].length) {
LIS[i] = LIS[j].slice();
}
}
LIS[i].push(arr[i]);
}
let LDS = new Array(n);
for (let i = 0; i < n; i++) {
LDS[i] = [];
}
LDS[n - 1].push(arr[n - 1]);
for (let i = n - 2; i >= 0; i--) {
for (let j = n - 1; j > i; j--) {
if (arr[j] < arr[i] && LDS[j].length > LDS[i].length) {
LDS[i] = LDS[j].slice();
}
}
LDS[i].push(arr[i]);
}
for (let i = 0; i < n; i++) {
LDS[i].reverse();
}
let max = 0;
let maxIndex = -1;
for (let i = 0; i < n; i++) {
if (LIS[i].length + LDS[i].length - 1 > max) {
max = LIS[i].length + LDS[i].length - 1;
maxIndex = i;
}
}
_print(LIS[maxIndex].slice(0, -1), LIS[maxIndex].length - 1);
_print(LDS[maxIndex], LDS[maxIndex].length);
}
const arr = [1, 11, 2, 10, 4, 5, 2, 1];
const n = arr.length;
printLBS(arr, n);
|
Output:
1 11 10 5 2 1
Time complexity of above Dynamic Programming solution is O(n2). Auxiliary space used by the program is O(n2). This article is contributed by Aditya Goel. If you like GeeksforGeeks and would like to contribute, you can also write an article using write.geeksforgeeks.org or mail your article to review-team@geeksforgeeks.org. See your article appearing on the GeeksforGeeks main page and help other Geeks. Please write comments if you find anything incorrect, or you want to share more information about the topic discussed above.