Longest Increasing Subsequence

Problem

Read an array. Print the length of the longest strictly increasing subsequence (the elements keep their order, but they do not have to be next to each other).

Constraints

  • 1 <= n <= 2000

Examples

Input

8
10 9 2 5 3 7 101 18

Output

4

Solutions

Best approachTime O(n log n)Space O(n)

The array tails stays sorted, so we can use binary search. A smaller tail is always better, because it is easier to extend. The length of tails is the answer. This takes O(n log n) instead of O(n^2).

Same code works in Java810172125Basic features only, so nothing changes between versions.
/*
 * Problem : Find the LIS length.
 * Input   : n, then n integers.
 * Output  : The length.
 * Approach: Keep the smallest tails.
 * Time    : O(n log n)
 * Space   : O(n)
 * Java    : 8 or newer.
 */
import java.util.Scanner;

public class Main {
    // This method solves the task. It receives the input and returns the answer.
    static int solve(int[] a) {
        // Step 1: tails[i] = the smallest last value of an increasing subsequence with length i + 1.
        int[] tails = new int[a.length];
        int length = 0;

        for (int value : a) {
            // Step 2: Binary search for the first tail that is greater than or equal to the value.
            int low = 0;
            int high = length;
            while (low < high) {
                int mid = low + (high - low) / 2;
                if (tails[mid] < value) {
                    low = mid + 1;
                } else {
                    high = mid;
                }
            }

            // Step 3: Replace that tail with the value. If the value is bigger than all tails, the subsequence grows.
            tails[low] = value;
            if (low == length) {
                length++;
            }
        }
        return length;
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int size = scanner.nextInt();
        int[] a = new int[size];
        for (int i = 0; i < size; i++) {
            a[i] = scanner.nextInt();
        }
        System.out.println(solve(a));
    }
}
Hints (2)
  1. Why is a smaller tail better?
  2. Why can we use binary search on tails?