Dynamic Programming Longest Increasing Sub Sequence

Longest Increasing Sub Sequence

Welcome to the 6th part of our discussion on Dynamic Programming. You can refer to the first article (introduction) here. In this article we are going to discuss the “Longest Increasing Sub Sequence” problem which can be solved efficiently using Dynamic Programming.

Problem Definition

Given a sequence of numbers [A1 .. An] we need to find a longest strictly increasing sub sequence not necessarily contiguous. For example if the input sequence is [9, 11, 2, 13, 7, 15] then the sequence [9, 11, 13, 15] is the longest strictly increasing sub sequence. Each number in the sub sequence is greater than the previous number and not necessarily contiguous.

Solution

Let us return back to our example sequence [9, 11, 2, 13, 7, 15]. Here are sample strictly increasing sub sequences:

Now notice the following:

The longest increasing sub sequence ending at position (1) is [9]
The longest increasing sub sequence ending at position (2) is [9, 11]
The longest increasing sub sequence ending at position (3) is [2]
The longest increasing sub sequence ending at position (4) is [9, 11, 13]
The longest increasing sub sequence ending at position (5) is [2, 7]
The longest increasing sub sequence ending at position (6) is [9, 11, 13, 15]

Now let us define L(j) to be the length of the longest increasing sub sequence ending at position (j):

Note that the general form is:

Let us apply the formula and see that it really works:

In plain English a sub sequence strictly increasing of maximum length ending at position (j) must be an extension to another sub sequence strictly increasing of maximum length ending at a previous position (i) where A[i] is less than A[j]. Note that the condition i < j and A[i] < A[j] means we might have more than one sub sequences satisfying this condition. In other words the element A[j] at position (j) can be an extension to more than one strictly increasing sub sequence but we take the sub sequence of maximum length that is why we we have MAX(L(i)) in the formula. Once the array L(j) is fully populated we must scan the whole array to find the (j) value with largest L(j). At each position (j) there is a maximum length strictly increasing sub sequence ending there but we are concerned about the one with maximum length across the whole input array. Printing out the individual elements that form the increasing sub sequence can be done using back pointers. We need to have a back pointers array b[j] to save the (i) value that satisfies the condition mention earlier:

Once b[j] is filled we can scan the table to print the actual numbers in the increasing sub sequence. We will demonstrate that in the last section of this article. We are ready now to proceed with our Dynamic Programming solution. Characterize the structure of an optimal solution

The value we are trying to optimize is a length value. We are intending to maximize the length of a strictly increasing sub sequence in the original input array. We already saw that a sub sequence of maximum length ending at some position (j) is an extension to another sub sequence strictly increasing ending at an earlier position (i). Since there might be more than one strictly increasing sub sequence depending on the value of (i) we take the longest one. Let us define that recursively in the following section.

Recursively define the value of an optimal solution

Let L(j) be the length of a strictly increasing sub sequence ending at position (j). We have shown that L(j) can be expressed as:

Note that the values L(i) for each (i) less than (j) overlap. These sub problems use each other for example L(3) is used in L(6), L(5) and L(4) as demonstrated before (You can go back to refresh your memory if you forgot). I will keep repeating this concept as it is very important to put in mind. In Dynamic Programming new values are computed while already computed values are only looked up in a table. In the next section we will build our tables in a bottom up fashion.

Compute the value of an optimal solution in a bottom up fashion

We already explained the Dynamic Programming solution in general. Below you can find the actual algorithm to populate L(j) and b(j) in pseudo code. Code is explained in details using comments. Take a look:

Construct an optimal solution from the computed information

The final step is to print out the individual elements that construct the solution. We achieve this using back pointers array b[j]. Note that the pseudo code below does that in reverse order. You can modify it on your own if you wish to print the elements in order.

We are done with part 6. In the next part I will explain the 6th Dynamic Programming problem “Making Change“. Please use the comments section below for questions, corrections or feedback. Thanks for reading.

15 Comments

Add a Comment

Your email address will not be published. Required fields are marked *