Length of longest strict bitonic subsequence

Last Updated : 13 Nov, 2025

Given an array arr[] of integers, find the length of the longest strict bitonic subsequence.

A subsequence is strict bitonic if it first strictly increases and then strictly decreases, such that the absolute difference between every pair of consecutive elements is exactly 1 both in the increasing and decreasing parts.

A fully increasing or fully decreasing subsequence (with consecutive differences of 1) is also considered bitonic.

Examples:

Input: arr[] = [4, 5, 6, 5, 4, 3]
Output: 6
Explanation: The longest strict bitonic subsequence is [4, 5, 6, 5, 4, 3]. Both the increasing [4, 5, 6] and decreasing [6, 5, 4, 3] parts have consecutive differences of 1.

Input: arr[] = [10, 9, 8, 7]
Output: 4
Explanation: The sequence is fully decreasing, which is allowed. Consecutive differences are 1: [10, 9, 8, 7].

[Naive Approach] Using Recursion - O(2n) Time and O(n) Space

The idea is to use recursion to generate all subsequences of the array. For each subsequence, check if it first strictly increases with consecutive differences of 1 and then strictly decreases with consecutive differences of 1. The length of the longest valid subsequence is the answer.

C++
//Driver Code Starts
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
//Driver Code Ends


// Function to check if a given subsequence is 
// strict bitonic with consecutive differences of 1
bool isStrictBitonic(const vector<int>& subseq) {
    int n = subseq.size();
    if (n == 0) return false;

    int i = 0;
    
    // Increasing part
    while (i + 1 < n && subseq[i + 1] - subseq[i] == 1) i++;

    // Decreasing part
    while (i + 1 < n && subseq[i] - subseq[i + 1] == 1) i++;

    // true if reached the end
    return i == n - 1; 
}

// Recursive function to generate all subsequences
void generateSubsequences(vector<int>& arr, int idx, 
                    vector<int>& subseq, int& maxLen) {
   
    if (idx == arr.size()) {
        if (isStrictBitonic(subseq)) {
            maxLen = max(maxLen, (int)subseq.size());
        }
        return;
    }

    // Include current element
    subseq.push_back(arr[idx]);
    generateSubsequences(arr, idx + 1, subseq, maxLen);

    // Exclude current element
    subseq.pop_back();
    generateSubsequences(arr, idx + 1, subseq, maxLen);
}

// Function to find the length of the 
// longest strict bitonic subsequence
int longestStrictBitonic(vector<int>& arr) {
    vector<int> subseq;
    int maxLen = 0;
   
    generateSubsequences(arr, 0, subseq, maxLen);
    return maxLen;
}


//Driver Code Starts
int main() {
    vector<int> arr = {4, 5, 6, 5, 4, 3};
    cout << longestStrictBitonic(arr) << endl;
    return 0;
}

//Driver Code Ends
Java
//Driver Code Starts
import java.util.Arrays;
import java.util.List;
import java.util.ArrayList;

class GFG {
//Driver Code Ends


    // Function to check if a given subsequence is
    // strict bitonic with consecutive differences of 1
    static boolean isStrictBitonic(List<Integer> subseq) {
        int n = subseq.size();
        if (n == 0) return false;

        int i = 0;

        // Increasing part
        while (i + 1 < n && subseq.get(i + 1) - subseq.get(i) == 1) i++;

        // Decreasing part
        while (i + 1 < n && subseq.get(i) - subseq.get(i + 1) == 1) i++;

        return i == n - 1;
    }

    // Recursive function to generate all subsequences
    static void generateSubsequences(int[] arr, int idx, 
                                     List<Integer> subseq, int[] maxLen) {
        if (idx == arr.length) {
            if (isStrictBitonic(subseq)) {
                maxLen[0] = Math.max(maxLen[0], subseq.size());
            }
            return;
        }

        // Include current element
        subseq.add(arr[idx]);
        generateSubsequences(arr, idx + 1, subseq, maxLen);

        // Exclude current element
        subseq.remove(subseq.size() - 1);
        generateSubsequences(arr, idx + 1, subseq, maxLen);
    }

    static int longestStrictBitonic(int[] arr) {
        List<Integer> subseq = new ArrayList<>();
        int[] maxLen = new int[]{0};
        generateSubsequences(arr, 0, subseq, maxLen);
        return maxLen[0];
    }

//Driver Code Starts

    public static void main(String[] args) {
        int[] arr = {4, 5, 6, 5, 4, 3};
        System.out.println(longestStrictBitonic(arr));
    }
}

//Driver Code Ends
Python
# Function to check if a given subsequence is 
# strict bitonic with consecutive differences of 1
def isStrictBitonic(subseq):
    n = len(subseq)
    if n == 0:
        return False

    i = 0

    # Increasing part
    while i + 1 < n and subseq[i + 1] - subseq[i] == 1:
        i += 1

    # Decreasing part
    while i + 1 < n and subseq[i] - subseq[i + 1] == 1:
        i += 1

    # True if reached the end
    return i == n - 1

# Recursive function to generate all subsequences
def generateSubsequences(arr, idx, subseq, maxLen):
    if idx == len(arr):
        if isStrictBitonic(subseq):
            maxLen[0] = max(maxLen[0], len(subseq))
        return

    # Include current element
    subseq.append(arr[idx])
    generateSubsequences(arr, idx + 1, subseq, maxLen)

    # Exclude current element
    subseq.pop()
    generateSubsequences(arr, idx + 1, subseq, maxLen)

# Function to find the length of the 
# longest strict bitonic subsequence
def longestStrictBitonic(arr):
    subseq = []
    maxLen = [0]
    generateSubsequences(arr, 0, subseq, maxLen)
    return maxLen[0]


#Driver Code Starts
if __name__ == '__main__':
    arr = [4, 5, 6, 5, 4, 3]
    print(longestStrictBitonic(arr))

#Driver Code Ends
C#
//Driver Code Starts
using System;
using System.Collections.Generic;

class GFG {
//Driver Code Ends


    // Function to check if a given subsequence is 
    // strict bitonic with consecutive differences of 1
    static bool isStrictBitonic(List<int> subseq) {
        int n = subseq.Count;
        if (n == 0) return false;

        int i = 0;

        // Increasing part
        while (i + 1 < n && subseq[i + 1] - subseq[i] == 1) i++;

        // Decreasing part
        while (i + 1 < n && subseq[i] - subseq[i + 1] == 1) i++;

        return i == n - 1;
    }
    
    // Recursive function to generate all subsequences
    static void generateSubsequences(int[] arr, int idx, 
                        List<int> subseq, ref int maxLen) {
        if (idx == arr.Length) {
            if (isStrictBitonic(subseq)) {
                maxLen = Math.Max(maxLen, subseq.Count);
            }
            return;
        }

        // Include current element
        subseq.Add(arr[idx]);
        generateSubsequences(arr, idx + 1, subseq, ref maxLen);

        // Exclude current element
        subseq.RemoveAt(subseq.Count - 1);
        generateSubsequences(arr, idx + 1, subseq, ref maxLen);
    }

    // Function to find the length of the 
    // longest strict bitonic subsequence
    static int longestStrictBitonic(int[] arr) {
        List<int> subseq = new List<int>();
        int maxLen = 0;
        generateSubsequences(arr, 0, subseq, ref maxLen);
        return maxLen;
    }

//Driver Code Starts

    static void Main() {
        int[] arr = {4, 5, 6, 5, 4, 3};
        Console.WriteLine(longestStrictBitonic(arr));
    }
}

//Driver Code Ends
JavaScript
// Function to check if a given subsequence is 
// strict bitonic with consecutive differences of 1
function isStrictBitonic(subseq) {
    const n = subseq.length;
    if (n === 0) return false;

    let i = 0;

    // Increasing part
    while (i + 1 < n && subseq[i + 1] - subseq[i] === 1) i++;

    // Decreasing part
    while (i + 1 < n && subseq[i] - subseq[i + 1] === 1) i++;

    return i === n - 1;
}

// Recursive function to generate all subsequences
function generateSubsequences(arr, idx, subseq, maxLen) {
    if (idx === arr.length) {
        if (isStrictBitonic(subseq)) {
            maxLen[0] = Math.max(maxLen[0], subseq.length);
        }
        return;
    }

    // Include current element
    subseq.push(arr[idx]);
    generateSubsequences(arr, idx + 1, subseq, maxLen);

    // Exclude current element
    subseq.pop();
    generateSubsequences(arr, idx + 1, subseq, maxLen);
}

// Function to find the length of the 
// longest strict bitonic subsequence
function longestStrictBitonic(arr) {
    const subseq = [];
    const maxLen = [0];
   
    generateSubsequences(arr, 0, subseq, maxLen);
    return maxLen[0];
}


//Driver Code Starts
// Driver Code
const arr = [4, 5, 6, 5, 4, 3];
console.log(longestStrictBitonic(arr));

//Driver Code Ends

Output
6

[Better Approach] Using Two Arrays - O(n2) Time and O(n) Space

We can solve this efficiently using the idea of longest bitonic subsequence instead of checking all subsequences.

First, for each index i, compute inc[i] as the length of the longest increasing subsequence ending at i, where consecutive elements differ by exactly 1. Similarly, compute dec[i] as the length of the longest decreasing subsequence starting at i with the same consecutive difference constraint.

The length of a strict bitonic subsequence with peak at i is inc[i] + dec[i] - 1. The final answer is the maximum of these values over all indices.

C++
//Driver Code Starts
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

//Driver Code Ends

int longestStrictBitonic(vector<int>& arr) {
    int n = arr.size();
    vector<int> inc(n, 1), dec(n, 1);

    // Compute longest increasing subsequence ending at each index
    for (int i = 1; i < n; i++) {
        for (int j = 0; j < i; j++) {
            if (arr[i] - arr[j] == 1) {
                inc[i] = max(inc[i], inc[j] + 1);
            }
        }
    }

    // Compute longest decreasing subsequence starting at each index
    for (int i = n - 2; i >= 0; i--) {
        for (int j = n - 1; j > i; j--) {
            if (arr[i] - arr[j] == 1) {
                dec[i] = max(dec[i], dec[j] + 1);
            }
        }
    }

    int maxLen = 0;
    for (int i = 0; i < n; i++) {
        maxLen = max(maxLen, inc[i] + dec[i] - 1);
    }

    return maxLen;
}

//Driver Code Starts

int main() {
    vector<int> arr = {4, 5, 6, 5, 4, 3};
    cout << longestStrictBitonic(arr) << endl; 
    return 0;
}

//Driver Code Ends
Java
//Driver Code Starts
import java.util.Arrays;

class GFG {
//Driver Code Ends


    static int longestStrictBitonic(int[] arr) {
        int n = arr.length;
        int[] inc = new int[n];
        int[] dec = new int[n];
        Arrays.fill(inc, 1);
        Arrays.fill(dec, 1);

        // Compute longest increasing subsequence ending at each index
        for (int i = 1; i < n; i++) {
            for (int j = 0; j < i; j++) {
                if (arr[i] - arr[j] == 1) {
                    inc[i] = Math.max(inc[i], inc[j] + 1);
                }
            }
        }

        // Compute longest decreasing subsequence starting at each index
        for (int i = n - 2; i >= 0; i--) {
            for (int j = n - 1; j > i; j--) {
                if (arr[i] - arr[j] == 1) {
                    dec[i] = Math.max(dec[i], dec[j] + 1);
                }
            }
        }

        int maxLen = 0;
        for (int i = 0; i < n; i++) {
            maxLen = Math.max(maxLen, inc[i] + dec[i] - 1);
        }
        return maxLen;
    }


//Driver Code Starts
    public static void main(String[] args) {
        int[] arr = {4, 5, 6, 5, 4, 3};
        System.out.println(longestStrictBitonic(arr));
    }
}

//Driver Code Ends
Python
def longestStrictBitonic(arr):
    n = len(arr)
    inc = [1] * n
    dec = [1] * n

    # Compute longest increasing subsequence ending at each index
    for i in range(1, n):
        for j in range(i):
            if arr[i] - arr[j] == 1:
                inc[i] = max(inc[i], inc[j] + 1)

    # Compute longest decreasing subsequence starting at each index
    for i in range(n - 2, -1, -1):
        for j in range(n - 1, i, -1):
            if arr[i] - arr[j] == 1:
                dec[i] = max(dec[i], dec[j] + 1)

    maxlen = 0
    for i in range(n):
        maxlen = max(maxlen, inc[i] + dec[i] - 1)

    return maxlen


#Driver Code Starts
if __name__ == '__main__':
    arr = [4, 5, 6, 5, 4, 3]
    print(longestStrictBitonic(arr))

#Driver Code Ends
C#
//Driver Code Starts
using System;

class GFG {
//Driver Code Ends

    
    static int longestStrictBitonic(int[] arr) {
        int n = arr.Length;
        int[] inc = new int[n];
        int[] dec = new int[n];
        for (int i = 0; i < n; i++) {
            inc[i] = dec[i] = 1;
        }

        // Compute longest increasing subsequence ending at each index
        for (int i = 1; i < n; i++) {
            for (int j = 0; j < i; j++) {
                if (arr[i] - arr[j] == 1) {
                    inc[i] = Math.Max(inc[i], inc[j] + 1);
                }
            }
        }

        // Compute longest decreasing subsequence starting at each index
        for (int i = n - 2; i >= 0; i--) {
            for (int j = n - 1; j > i; j--) {
                if (arr[i] - arr[j] == 1) {
                    dec[i] = Math.Max(dec[i], dec[j] + 1);
                }
            }
        }

        int maxLen = 0;
        for (int i = 0; i < n; i++) {
            maxLen = Math.Max(maxLen, inc[i] + dec[i] - 1);
        }
        return maxLen;
    }


//Driver Code Starts
    static void Main() {
        int[] arr = {4, 5, 6, 5, 4, 3};
        Console.WriteLine(longestStrictBitonic(arr));
    }
}

//Driver Code Ends
JavaScript
function longestStrictBitonic(arr) {
    let n = arr.length;
    let inc = Array(n).fill(1);
    let dec = Array(n).fill(1);

    // Compute longest increasing subsequence ending at each index
    for (let i = 1; i < n; i++) {
        for (let j = 0; j < i; j++) {
            if (arr[i] - arr[j] === 1) {
                inc[i] = Math.max(inc[i], inc[j] + 1);
            }
        }
    }

    // Compute longest decreasing subsequence starting at each index
    for (let i = n - 2; i >= 0; i--) {
        for (let j = n - 1; j > i; j--) {
            if (arr[i] - arr[j] === 1) {
                dec[i] = Math.max(dec[i], dec[j] + 1);
            }
        }
    }

    let maxLen = 0;
    for (let i = 0; i < n; i++) {
        maxLen = Math.max(maxLen, inc[i] + dec[i] - 1);
    }

    return maxLen;
}


//Driver Code Starts
// Driver Code
let arr = [4, 5, 6, 5, 4, 3];
console.log(longestStrictBitonic(arr));

//Driver Code Ends

Output
6

[Expected Approach] Using HashMap - O(n) Time and O(n) Space

 The idea is to use hashmaps to quickly find the longest increasing and decreasing subsequences that follow the consecutive difference rule (difference of 1).

  • Traverse the array from left to right to fill inc[i], where inc[i] = 1 + inc of the previous element arr[i] - 1 (if it exists).
  • Traverse from right to left to fill dec[i], where dec[i] = 1 + dec of the next element arr[i] - 1 (if it exists).
  • Use hashmaps to store the best length seen so far for each value, ensuring O(1) average lookup.
  • Finally, the longest strict bitonic subsequence passing through index i is inc[i] + dec[i] - 1.

The answer is the maximum of this value across all indices.

C++
//Driver Code Starts
#include <iostream>
#include <vector>
#include <unordered_map>
#include <algorithm>
using namespace std;
//Driver Code Ends


int longestStrictBitonic(vector<int>& arr) {
    int n = arr.size();
    vector<int> inc(n, 1), dec(n, 1);
    unordered_map<int, int> left, right;

    // Compute increasing lengths
    for (int i = 0; i < n; i++) {
        if (left.count(arr[i] - 1))
            inc[i] = left[arr[i] - 1] + 1;
        left[arr[i]] = max(left[arr[i]], inc[i]);
    }

    // Compute decreasing lengths
    for (int i = n - 1; i >= 0; i--) {
        if (right.count(arr[i] - 1))
            dec[i] = right[arr[i] - 1] + 1;
        right[arr[i]] = max(right[arr[i]], dec[i]);
    }

    int ans = 0;
    for (int i = 0; i < n; i++)
        ans = max(ans, inc[i] + dec[i] - 1);
    return ans;
}

//Driver Code Starts

int main() {
    vector<int> arr = {4, 5, 6, 5, 4, 3};
    cout << longestStrictBitonic(arr);
    return 0;
}

//Driver Code Ends
Java
//Driver Code Starts
import java.util.HashMap;
import java.util.Map;
import java.util.Arrays;

class GFG {
//Driver Code Ends

    static int longestStrictBitonic(int[] arr) {
        int n = arr.length;
        int[] inc = new int[n];
        int[] dec = new int[n];
        Arrays.fill(inc, 1);
        Arrays.fill(dec, 1);

        Map<Integer, Integer> left = new HashMap<>();
        Map<Integer, Integer> right = new HashMap<>();

        // Increasing part
        for (int i = 0; i < n; i++) {
            if (left.containsKey(arr[i] - 1))
                inc[i] = left.get(arr[i] - 1) + 1;
            left.put(arr[i], Math.max(left.getOrDefault(arr[i], 0), inc[i]));
        }

        // Decreasing part
        for (int i = n - 1; i >= 0; i--) {
            if (right.containsKey(arr[i] - 1))
                dec[i] = right.get(arr[i] - 1) + 1;
            right.put(arr[i], Math.max(right.getOrDefault(arr[i], 0), dec[i]));
        }

        int ans = 0;
        for (int i = 0; i < n; i++)
            ans = Math.max(ans, inc[i] + dec[i] - 1);
        return ans;
    }

//Driver Code Starts

    public static void main(String[] args) {
        int[] arr = {4, 5, 6, 5, 4, 3};
        System.out.println(longestStrictBitonic(arr));
    }
}

//Driver Code Ends
Python
def longeststrictbitonic(arr):
    n = len(arr)
    inc = [1] * n
    dec = [1] * n
    left = {}
    right = {}

    # Increasing part
    for i in range(n):
        if arr[i] - 1 in left:
            inc[i] = left[arr[i] - 1] + 1
        left[arr[i]] = max(left.get(arr[i], 0), inc[i])

    # Decreasing part
    for i in range(n - 1, -1, -1):
        if arr[i] - 1 in right:
            dec[i] = right[arr[i] - 1] + 1
        right[arr[i]] = max(right.get(arr[i], 0), dec[i])

    ans = 0
    for i in range(n):
        ans = max(ans, inc[i] + dec[i] - 1)
    return ans


#Driver Code Starts
if __name__ == '__main__':
    arr = [4, 5, 6, 5, 4, 3]
    print(longeststrictbitonic(arr))

#Driver Code Ends
C#
//Driver Code Starts
using System;
using System.Collections.Generic;

class GFG {
//Driver Code Ends

    
    static int longestStrictBitonic(int[] arr) {
        int n = arr.Length;
        int[] inc = new int[n];
        int[] dec = new int[n];
        Array.Fill(inc, 1);
        Array.Fill(dec, 1);

        Dictionary<int, int> left = new Dictionary<int, int>();
        Dictionary<int, int> right = new Dictionary<int, int>();

        // Increasing part
        for (int i = 0; i < n; i++) {
            if (left.ContainsKey(arr[i] - 1))
                inc[i] = left[arr[i] - 1] + 1;
                
            left[arr[i]] = Math.Max(left.GetValueOrDefault(arr[i], 0), inc[i]);
        }

        // Decreasing part
        for (int i = n - 1; i >= 0; i--) {
            if (right.ContainsKey(arr[i] - 1))
                dec[i] = right[arr[i] - 1] + 1;
                
            right[arr[i]] = Math.Max(right.GetValueOrDefault(arr[i], 0), dec[i]);
        }

        int ans = 0;
        for (int i = 0; i < n; i++)
            ans = Math.Max(ans, inc[i] + dec[i] - 1);
        return ans;
    }

//Driver Code Starts

    static void Main() {
        int[] arr = {4, 5, 6, 5, 4, 3};
        Console.WriteLine(longestStrictBitonic(arr));
    }
}

//Driver Code Ends
JavaScript
function longestStrictBitonic(arr) {
    let n = arr.length;
    let inc = Array(n).fill(1);
    let dec = Array(n).fill(1);
    let left = new Map();
    let right = new Map();

    // Increasing part
    for (let i = 0; i < n; i++) {
      if (left.has(arr[i] - 1))
        inc[i] = left.get(arr[i] - 1) + 1;
      
      left.set(arr[i], Math.max(left.get(arr[i]) || 0, inc[i]));
    }

    // Decreasing part
    for (let i = n - 1; i >= 0; i--) {
      if (right.has(arr[i] - 1))
        dec[i] = right.get(arr[i] - 1) + 1;
        
      right.set(arr[i], Math.max(right.get(arr[i]) || 0, dec[i]));
    }

    let ans = 0;
    for (let i = 0; i < n; i++)
      ans = Math.max(ans, inc[i] + dec[i] - 1);
    return ans;
}


//Driver Code Starts
// Driver Code
const arr = [4, 5, 6, 5, 4, 3];
console.log(longestStrictBitonic(arr));

//Driver Code Ends

Output
6


Comment