Great resources have been mentioned in the comments; if you still want a piece of python code, I've written and explained it here.
In this algorithm, for all L < N
, we keep a track of the values in the input which represent the endpoint of the current longest increasing subsequence of length L
.
longest_sequence_values
stores these values. For example, longest_sequence_values[3]
is a value in the input at which the longest increasing subsequence of length 3 ends.
Note here that longest_sequence_values
will always be non-decreasing, which allows us to perform a binary search when we're trying to build a new longest increasing subsequence. This happens because if i < j
, then the endpoint of a subsequence of length i
cannot be larger than the endpoint of a subsequence of length j
.
longest_current_sequence
is the length of of the longest subsequence found thus far. We need this value to specify the range of the binary search. It also represents the answer at the end.
from math import ceil
N = int(input())
input_vals = []
for i in range(N):
input_vals.append(input())
longest_sequence_values = [None] * (N+1)
longest_current_sequence = 0
for i in range(N):
# binary search starts here
# this gives us the log(N) factor
lo = 1
hi = longest_current_sequence
while lo <= hi:
mid = int(ceil((lo+hi)/2))
if longest_sequence_values[mid] <= input_vals[i]:
lo = mid + 1
else:
hi = mid - 1
# lo will be length of the longest sequence ending at input_vals[i]
longest_len_here = lo
# We have a new lis of length longest_len_here ending at index i
# Note that before we perform the following substitutions,
# longest_sequence_values[longest_len_here] >= input_vals[i]
# This means that the new endpoint of the lis of length longest_len_here
# is <= to the old endpoint.
# This point is essential in keeping the result optimal
longest_sequence_values[longest_len_here] = input_vals[i]
if longest_len_here > longest_current_sequence:
longest_current_sequence = longest_len_here
print longest_current_sequence