← Back to DSA Course | Chapter 14: Dynamic Programming | Lesson 7 of 12

Longest Increasing Subsequence

LIS Idea

The longest increasing subsequence of an array is the longest run of elements, not necessarily adjacent, that keeps strictly increasing in value as you read left to right through the original array.

Example: LIS Idea

#include <iostream>
using namespace std;
int main() {
	int arr[] = {3,1,4,1,5,9,2,6};
	cout << "Longest strictly increasing run (not necessarily adjacent): 1,4,5,9 has length 4";
	return 0;
}
public class Main {
	public static void main(String[] args) {
		int[] arr = {3,1,4,1,5,9,2,6};
		System.out.println("Longest strictly increasing run (not necessarily adjacent): 1,4,5,9 has length 4");
	}
}
arr = [3,1,4,1,5,9,2,6]
print("Longest strictly increasing run (not necessarily adjacent): 1,4,5,9 has length 4")
#include <stdio.h>
int main() {
	int arr[] = {3,1,4,1,5,9,2,6};
	printf("Longest strictly increasing run (not necessarily adjacent): 1,4,5,9 has length 4");
	return 0;
}

DP State

dp[i] stores the length of the longest increasing subsequence that ends exactly at index i, so the overall answer is simply the largest value found anywhere in the dp array once it's fully computed.

Example: DP State

#include <iostream>
using namespace std;
int main() {
	int arr[] = {3,1,4,1,5,9,2,6};
	int dp[8]; for (int i=0;i<8;i++) dp[i]=1;
	cout << "dp[i] = LIS length ending exactly at index i, initialized to 1 (each element alone)";
	return 0;
}
public class Main {
	public static void main(String[] args) {
		int[] arr = {3,1,4,1,5,9,2,6};
		int[] dp = new int[8];
		java.util.Arrays.fill(dp, 1);
		System.out.println("dp[i] = LIS length ending exactly at index i, initialized to 1 (each element alone)");
	}
}
arr = [3,1,4,1,5,9,2,6]
dp = [1] * 8
print("dp[i] = LIS length ending exactly at index i, initialized to 1 (each element alone)")
#include <stdio.h>
int main() {
	int arr[] = {3,1,4,1,5,9,2,6};
	int dp[8]; for (int i=0;i<8;i++) dp[i]=1;
	printf("dp[i] = LIS length ending exactly at index i, initialized to 1 (each element alone)");
	return 0;
}

Transition

For each index i, you look back at every earlier index j: if the value at j is smaller than the value at i, index i could extend that subsequence, so dp[i] becomes the largest dp[j] + 1 among all such valid j.

Example: Transition

#include <iostream>
using namespace std;
int main() {
	int arr[] = {3,1,4,1,5,9,2,6};
	int dp[8]; for (int i=0;i<8;i++) dp[i]=1;
	int best = 1;
	for (int i = 1; i < 8; i++) {
		for (int j = 0; j < i; j++)
			if (arr[j] < arr[i]) dp[i] = max(dp[i], dp[j]+1);
		best = max(best, dp[i]);
	}
	cout << "Longest increasing subsequence length: " << best;
	return 0;
}
public class Main {
	public static void main(String[] args) {
		int[] arr = {3,1,4,1,5,9,2,6};
		int[] dp = new int[8];
		java.util.Arrays.fill(dp, 1);
		int best = 1;
		for (int i = 1; i < 8; i++) {
			for (int j = 0; j < i; j++)
				if (arr[j] < arr[i]) dp[i] = Math.max(dp[i], dp[j]+1);
			best = Math.max(best, dp[i]);
		}
		System.out.println("Longest increasing subsequence length: " + best);
	}
}
arr = [3,1,4,1,5,9,2,6]
dp = [1]*8
best = 1
for i in range(1, 8):
    for j in range(i):
        if arr[j] < arr[i]:
            dp[i] = max(dp[i], dp[j]+1)
    best = max(best, dp[i])
print("Longest increasing subsequence length:", best)
#include <stdio.h>
int main() {
	int arr[] = {3,1,4,1,5,9,2,6};
	int dp[8]; for (int i=0;i<8;i++) dp[i]=1;
	int best = 1;
	for (int i = 1; i < 8; i++) {
		for (int j = 0; j < i; j++)
			if (arr[j] < arr[i] && dp[j]+1 > dp[i]) dp[i] = dp[j]+1;
		if (dp[i] > best) best = dp[i];
	}
	printf("Longest increasing subsequence length: %d", best);
	return 0;
}

Reconstruction

Keeping track of which earlier index each dp[i] extended from lets you walk backward from the best ending index and rebuild the actual sequence of values, not just report how long it is.

Example: Reconstruction

#include <iostream>
using namespace std;
int main() {
	int arr[] = {3,1,4,1,5};
	int dp[5], from[5];
	for (int i=0;i<5;i++) { dp[i]=1; from[i]=-1; }
	for (int i = 1; i < 5; i++)
		for (int j = 0; j < i; j++)
			if (arr[j] < arr[i] && dp[j]+1 > dp[i]) { dp[i]=dp[j]+1; from[i]=j; }
	cout << "from[] lets you walk backward from the best-ending index to rebuild the actual sequence";
	return 0;
}
public class Main {
	public static void main(String[] args) {
		int[] arr = {3,1,4,1,5};
		int[] dp = new int[5], from = new int[5];
		java.util.Arrays.fill(dp, 1);
		java.util.Arrays.fill(from, -1);
		for (int i = 1; i < 5; i++)
			for (int j = 0; j < i; j++)
				if (arr[j] < arr[i] && dp[j]+1 > dp[i]) { dp[i]=dp[j]+1; from[i]=j; }
		System.out.println("from[] lets you walk backward from the best-ending index to rebuild the actual sequence");
	}
}
arr = [3,1,4,1,5]
dp = [1]*5
frm = [-1]*5
for i in range(1, 5):
    for j in range(i):
        if arr[j] < arr[i] and dp[j]+1 > dp[i]:
            dp[i] = dp[j]+1
            frm[i] = j
print("from[] lets you walk backward from the best-ending index to rebuild the actual sequence")
#include <stdio.h>
int main() {
	int arr[] = {3,1,4,1,5};
	int dp[5], from[5];
	for (int i=0;i<5;i++) { dp[i]=1; from[i]=-1; }
	for (int i = 1; i < 5; i++)
		for (int j = 0; j < i; j++)
			if (arr[j] < arr[i] && dp[j]+1 > dp[i]) { dp[i]=dp[j]+1; from[i]=j; }
	printf("from[] lets you walk backward from the best-ending index to rebuild the actual sequence");
	return 0;
}

Practice

LIS-style reasoning shows up in problems like finding the longest chain of nested boxes or the best sequence of non-decreasing stock trades, anywhere you need the longest valid ordering hidden inside a larger sequence.

Example: Practice

#include <iostream>
using namespace std;
int main() {
	cout << "LIS reasoning: longest chain of nested boxes, best sequence of non-decreasing stock trades";
	return 0;
}
public class Main {
	public static void main(String[] args) {
		System.out.println("LIS reasoning: longest chain of nested boxes, best sequence of non-decreasing stock trades");
	}
}
print("LIS reasoning: longest chain of nested boxes, best sequence of non-decreasing stock trades")
#include <stdio.h>
int main() {
	printf("LIS reasoning: longest chain of nested boxes, best sequence of non-decreasing stock trades");
	return 0;
}

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.