LIS (Longest Increasing Subsequence)

Concept

LIS finds the length of the longest strictly increasing subsequence in an array. The O(n log n) approach maintains a list where p[i] is the smallest tail element of all increasing subsequences of length i+1. Use binary search (lower_bound) to find where each new element fits.

When to Use

  • Finding the longest increasing subsequence
  • Problems reducible to LIS (e.g., box stacking, longest chain)
  • When O(n^2) DP is too slow and you need O(n log n)
  • Patience sorting

Intuition

Maintain a list of 'best possible tails'. For each new number, if it's larger than everything in the list, extend the list. Otherwise, find the first element >= it and replace that element. This keeps the tails as small as possible, maximizing future extension potential. The list length at the end = LIS length. Think of it as playing patience (card game) — each pile's top card is as small as possible.

Complexity

Time
O(n log n)
Space
O(n)

Template Code

cpp
#include<bits/stdc++.h>
using namespace std;

int main(){
    ios_base::sync_with_stdio(0), cin.tie(0);

    int n;
    cin >> n;

    vector<int> nums(n);
    for(int i = 0; i < n; i++) cin >> nums[i];

    vector<int> p;
    for(int x : nums){
        int idx = lower_bound(p.begin(), p.end(), x) - p.begin();
        if(idx == p.size()) p.push_back(x);
        else p[idx] = x;
    }

    cout << p.size() << "\n";

    return 0;
}

// Sample Input:
// 8
// 10 9 2 5 3 7 101 18
//
// Sample Output:
// 4