300. Longest Increasing Subsequence

Find the longest increasing subsequence of a given sequence / array.

  • Subsequence: a sequence derived from another by deleting some elements without changing order.

E.g.:
Input : [0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15]

Output : 6

Method 1 DP (N^2 time, N space):

  • Compute the longest increasing subsequence ending at ith position ending at all i.
  • Use answers ending on (i-1), (i-2), ... to find the answer on ith.
    • Look for the lis from previous computed results
def list(self, A):
    dp = [1] * len(A) #dp array to store computed results, base value is 1.
    for i in range(1, len(A)):
        for j in range(0, i):
            if A[j] < A[i]:
                dp[i] = max(dp[i], dp[j]+1)
    return dp[i]

Method 2 DP(Nlog(n) time, 1 space)

tailsis an array storing the smallest tail of all increasing subsequences with lengthi+1intails[i].
For example, say we havenums = [4,5,6,3], then all the available increasing subsequences are:

len = 1   :      [4], [5], [6], [3]   => tails[0] = 3
len = 2   :      [4, 5], [5, 6]       => tails[1] = 5
len = 3   :      [4, 5, 6]            => tails[2] = 6

We can easily prove that tails is a increasing array. Therefore it is possible to do a binary search in tails array to find the one needs update.

Each time we only do one of the two:

(1) if x is larger than all tails, append it, increase the size by 1
(2) if tails[i-1] < x <= tails[i], update tails[i]

Doing so will maintain the tails invariant. The the final answer is just the size.

def lengthOfLIS(self, nums):
    tails = [0] * len(nums)
    size = 0
    for x in nums:
        i, j = 0, size
        while i != j:
            m = (i + j) / 2
            if tails[m] < x:
                i = m + 1
            else:
                j = m
        tails[i] = x
        size = max(i + 1, size)
    return size

Follow up: Find a LIS that has the largest sum.

Method 1 DP (N^2 time, N space):

  • For each entry in the dp list, store extra information about its sum along with its lis up to that point.
    • we can use a list: [[x1, y1],[x2,y2]] -> xi would be the lis up to i, yi would be the sum of LIS up to yi.
def lis(self, A):
    dp = [[1,i] for i in A] # sum from each position is at least its own value
    for i in range(1, len(A)):
        for j in range(0, i):
            if A[j] < A[i] and dp[i][0] <= dp[j][0]+1 and dp[i][1] < dp[j][1]+A[i]:
                dp[i][0], dp[i][1] = dp[j][0]+1, dp[j][1]+A[i]
    return max(dp, key=lambda x:x[1])[1]

dpLen[i] = maximum length of a LIS with maximum sum ending at i
dpSum[i] = maximum sum of a LIS with maximum sum ending at i

for i = 0 to n do
  dpLen[i] = 1
  dpSum[i] = input[i]

  maxLen = 0
  for j = 0 to i do
    if dpLen[j] > maxLen and input[j] < input[i]
      maxLen = dpLen[j]

  for j = 0 to i do
    if dpLen[j] == maxLen and input[j] < input[i] and dpSum[j] + input[i] > dpSum[i]
      dpSum[i] = dpSum[j] + input[i]

  dpLen[i] = maxLen + 1

results matching ""

    No results matching ""