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].
Table of Content
[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.
//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
//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
# 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
//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
// 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.
//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
//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
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
//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
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.
//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
//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
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
//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
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