Dynamic Programming · Medium
206. Longest Increasing Subsequence
Topics: Dynamic Programming, Binary Search
Solve it on DeVert Campus - free code runner
Problem
Given an array of n integers, find the length of its longest strictly increasing subsequence.
Input and output
Input: The first line contains an integer n. The second line contains n space-separated integers, the array.
Output: Print a single integer - the length of the longest strictly increasing subsequence.
Example
Input: 8 10 9 2 5 3 7 101 18 Output: 4
Constraints
1 <= n <= 10^5
Key observation
The answer is the MAXIMUM across the ENTIRE dp array, not just its last entry - the longest increasing subsequence doesn't have to end at the array's final element, an easy detail to overlook.
Intuition
Find the length of the longest strictly increasing subsequence within an array.
Brute force: Try every possible subsequence (2^n of them) and check which are increasing, taking the longest - this is exponential, impractical for n up to 10^5.
Optimised: The O(n²) DP checks every pair (i,j) once, reusing already-computed dp[j] values rather than re-deriving subsequences from scratch - and an even faster O(n log n) approach maintains a list of 'smallest tail value achievable for each subsequence length', using binary search to find where each new element fits, avoiding the full O(n²) pairwise comparison entirely.
Brute Force - Try Every Subsequence
Recursively decide, for each element, whether to include it in the subsequence or skip it, only allowing an inclusion if it's strictly greater than the last included element, and take the maximum length achieved over all valid choices. This directly checks every one of the 2^n possible subsequences for the increasing property, guaranteeing correctness, but is exponential and infeasible for anything but very small arrays.
Time: O(2^n)
Space: O(n)
Optimal - Patience Sorting with Binary Search
Maintain an array 'tails' where tails[k] holds the smallest possible tail value of any increasing subsequence of length k+1 found so far; for each new number, binary search tails for the first entry greater than or equal to it and overwrite that position (or append if it's larger than everything currently in tails). The final length of tails is the LIS length, since keeping the smallest possible tail for each subsequence length maximizes the chance of extending it later; using binary search to place each element turns the O(n) inner scan of the DP approach into an O(log n) lookup, which is essential given n can be up to 10^5.
Time: O(n log n)
Space: O(n)
Better - O(n^2) Dynamic Programming
Define dp[i] as the length of the longest increasing subsequence that ends exactly at index i, computed as 1 plus the maximum dp[j] over all j < i where array[j] < array[i] (or just 1 if no such j exists). Filling this array left to right and taking its maximum value avoids re-exploring overlapping subsequence choices from scratch, cutting the exponential brute force down to a quadratic number of comparisons.
Time: O(n^2)
Space: O(n)
Step by step
1. dp[i] = length of the longest increasing subsequence ENDING exactly at index i.
2. For each i, look at every earlier index j with arr[j] < arr[i], and take dp[i] = max(dp[i], dp[j] + 1).
3. The answer is the MAXIMUM value anywhere in the dp array, not necessarily dp[n-1].
Complexity
Time: O(n²) for the straightforward DP; O(n log n) with the tails-array-plus-binary-search optimization.
Space: O(n), for the dp array (or the tails array in the optimized approach).
Edge cases
Test n = 1 (answer 1), a strictly decreasing array (answer 1), and n at the upper bound of 10^5, which requires the O(n log n) approach rather than O(n^2).
Interview tip
Be ready to present BOTH the O(n²) DP and the O(n log n) optimized approach - interviewers frequently ask for the faster one as a follow-up, given how iconic this problem is.