1. Longest Increasing Subsequence
Acknowledgements
Some of this material is borrowed from Jeff Erickson's chapters on Recursion, Backtracking, and Dynamic Programming. Check them out for a more detailed comparison between recursive and memoized implementations.
The Problem
For any sequence , a subsequence of is another sequence obtained from by deleting zero or more elements, without changing the order of the remaining elements; the elements of the subsequence need not be contiguous in .
For example, when you drive down a major street in any city, you drive through a sequence of intersections with traffic lights, but you only have to stop at a subsequence of those intersections, where the traffic lights are red. If you're very lucky, you never stop at all: the empty sequence is a subsequence of . On the other hand, if you're very unlucky, you may have to stop at every intersection: is a subsequence of itself.
Here are some examples:
-
BOATis a subsequence ofBOX-OF-CHOCOLATES. -
OOFis a subsequence ofBOX-OF-CHOCOLATES, butOO-Fis not. -
O-O-OOLAis a subsequence ofBOX-OF-CHOCOLATES, butFOXis not. -
FOOLandBHOLAare subsequences ofBOX-OF-CHOCOLATES, butLAXandBETAare not.
Now suppose we are given a sequence of integers, and we need to find the length of a longest subsequence whose elements are in increasing order:
-
the input is an integer array , and
-
we need to compute the length of a longest possible sequence of indices such that for all .
We will use LIS to refer to the phrase longest increasing subsequence. Note that there may be multiple increasing subsequences, and even multiple choices for increasing subsequences that are the longest. The approaches below that work correctly to find the length of a LIS are easily adaptable to finding a LIS as well.
The Solution --- Take 0 (a dead end)
What are the fragments (AKA, what do we want to store)?
For any , let denote the subarray of starting at , and let denote the length of a LIS of . The 's will be our fragments.
Are the fragments going to be useful (AKA, where is the final answer)?
is evidently the answer we are looking for!
Do we have a kickstart (AKA, what are the base cases)?
, since there is only one increasing subsequence of : no competition.
How do the fragments come together (AKA, how do we compute the values that we have agreed to store)?
Consider the issue of computing the value of . Let be the set of all the longest increasing subsequences of . Let the common length of all the subsequences in be . We want to determine the value of .
Say we know the value of to be . Observe that :
the first inequality follows from the fact that every LIS of is also an increasing subsequence of , and
the second inequality follows from the fact that if there was a LIS of of length or more, then the tail of this subsequence would be an increasing subsequence of length at least of , which contradicts .
So all we need to figure out is whether the value of should be incremented or not. Unfortunately, by just staring at the value of , we have no way of knowing if is full of useless subsequences or not.
Can we put the pieces together without getting stuck (AKA, are the dependencies in step #4 acyclic)?
Not relevant, since step #4 is unspecified.
The Solution --- Take 1 (building on the bad take)
What are the fragments (AKA, what do we want to store)?
For any , let denote the set of increasing subsequences of that start with an element larger than . Now, let denote the length of a longest sequence in . The 's will be our fragments.
Are the fragments going to be useful (AKA, where is the final answer)?
is the answer we are looking for: since we don't know where the first element of an optimal solution is located in an array, we hazard a guess properly by taking a over all possibilities.
You could alternatively artificially expand the array to elements, with the last elements being the same as , and the first element being smaller than : then the final answer is simply .
Do we have a kickstart (AKA, what are the base cases)?
For all , we have:
if .
if .
How do the fragments come together (AKA, how do we compute the values that we have agreed to store)?
Consider computing for and .
First, suppose . Then .
On the other hand, suppose . Then:
Can we put the pieces together without getting stuck (AKA, are the dependencies in step #4 acyclic)?
Note that computing involves pinging and . So as long as all entries are computed before we get to , we are covered. One valid sequence computes and so on.

The Solution --- Take 2 (a different approach)
What are the fragments (AKA, what do we want to store)?
Let denote the set of all increasing subsequences of that start at and let denote the length of a LIS in . The 's will be our fragments.
Are the fragments going to be useful (AKA, where is the final answer)?
is the answer we are looking for.
Do we have a kickstart (AKA, what are the base cases)?
Clearly, .
How do the fragments come together (AKA, how do we compute the values that we have agreed to store)?
We know that any optimal sequence of interest starts at . We don't know , so we guess it by going over all possible choices: all such that .
Note that if the is taken over an empty set, then the output of is .
Can we put the pieces together without getting stuck (AKA, are the dependencies in step #4 acyclic)?
Computing involves pinging for . Compute in the order .
Time and Space Complexity
For Take 1:
-
We filled out entries at cost each, so the overall running time is .
-
We store entries, although with a careful implementation the storage can be limited to .
For Take 2:
-
We filled out entries at cost each, so the overall running time is again.
-
We store values.
Correctness
Correctness of the DP (Take 2)
Theorem. Let be given by:
and let denote the length of a LIS in . Then .
Proof.
Part 1. The DP stores a value that is achievable.
We show by backward induction over . The base case is witnessed by . For the induction step, if the set is empty then . Otherwise, let witness the . By induction there is a subsequence of length starting at ; prepend to get a subsequence of length .
Part 2. The value computed by the DP cannot be beaten.
We show by backward induction. Suppose for contradiction there is a subsequence of length . Let be the first element of its tail. Then 's tail belongs to with and , so by induction . Thus:
a contradiction.