15.4 Longest common subsequence
15.4-1
Determine an of and .
or .
15.4-2
Give pseudocode to reconstruct an from the completed table and the original sequences and in time, without using the table.
PRINT-LCS(c, X, Y, i, j)
if c[i, j] == 0
return
if X[i] == Y[j]
PRINT-LCS(c, X, Y, i - 1, j - 1)
print X[i]
else if c[i - 1, j] > c[i, j - 1]
PRINT-LCS(c, X, Y, i - 1, j)
else
PRINT-LCS(c, X, Y, i, j - 1)
15.4-3
Give a memoized version of that runs in time.
MEMOIZED-LCS-LENGTH(X, Y, i, j)
if c[i, j] > -1
return c[i, j]
if i == 0 or j == 0
return c[i, j] = 0
if x[i] == y[j]
return c[i, j] = LCS-LENGTH(X, Y, i - 1, j - 1) + 1
return c[i, j] = max(LCS-LENGTH(X, Y, i - 1, j), LCS-LENGTH(X, Y, i, j - 1))
15.4-4
Show how to compute the length of an using only entries in the table plus additional space. Then show how to do the same thing, but using entries plus additional space.
Since we only use the previous row of the table to compute the current row, we compute as normal, but when we go to compute row , we free row since we will never need it again to compute the length. To use even less space, observe that to compute , all we need are the entries , , and . Thus, we can free up entry-by-entry those from the previous row which we will never need again, reducing the space requirement to . Computing the next entry from the three that it depends on takes time and space.
15.4-5
Give an -time algorithm to find the longest monotonically increasing subsequence of a sequence of numbers.
Given a list of numbers , make a copy of called and then sort .
PRINT-LCS(c, X, Y)
n = c[X.length, Y.length]
let s[1..n] be a new array
i = X.length
j = Y.length
while i > 0 and j > 0
if x[i] == y[j]
s[n] = x[i]
n = n - 1
i = i - 1
j = j - 1
else if c[i - 1, j] ≥ c[i, j - 1]
i = i - 1
else j = j - 1
for i = 1 to s.length
print s[i]
MEMO-LCS-LENGTH-AUX(X, Y, c, b)
m = |X|
n = |Y|
if c[m, n] != 0 or m == 0 or n == 0
return
if x[m] == y[n]
b[m, n] = ↖
c[m, n] = MEMO-LCS-LENGTH-AUX(X[1..m - 1], Y[1..n - 1], c, b) + 1
else if MEMO-LCS-LENGTH-AUX(X[1..m - 1], Y, c, b) ≥ MEMO-LCS-LENGTH-AUX(X, Y[1..n - 1], c, b)
b[m, n] = ↑
c[m, n] = MEMO-LCS-LENGTH-AUX(X[1..m - 1], Y, c, b)
else
b[m, n] = ←
c[m, n] = MEMO-LCS-LENGTH-AUX(X, Y[1..n - 1], c, b)
MEMO-LCS-LENGTH(X, Y)
let c[1..|X|, 1..|Y|] and b[1..|X|, 1..|Y|] be new tables
MEMO-LCS-LENGTH-AUX(X, Y, c, b)
return c and b
Then, just run the algorithm on these two lists. The longest common subsequence must be monotone increasing because it is a subsequence of which is sorted. It is also the longest monotone increasing subsequence because being a subsequence of only adds the restriction that the subsequence must be monotone increasing. Since , and sorting can be done in time, the final running time will be .
15.4-6
Give an -time algorithm to find the longest monotonically increasing subsequence of a sequence of numbers. ( Observe that the last element of a candidate subsequence of length is at least as large as the last element of a candidate subsequence of length . Maintain candidate subsequences by linking them through the input sequence.)
The algorithm returns the longest monotonically increasing subsequence of , where has length .
The algorithm works as follows: a new array B will be created such that contains the last value of a longest monotonically increasing subsequence of length . A new array will be such that contains the monotonically increasing subsequence of length with smallest last element seen so far.
To analyze the runtime, observe that the entries of are in sorted order, so we can execute line 9 in time. Since every other line in the for-loop takes constant time, the total run-time is .
LONG-MONOTONIC(A)
let B[1..n] be a new array where every value = ∞
let C[1..n] be a new array
L = 1
for i = 1 to n
if A[i] < B[1]
B[1] = A[i]
C[1].head.key = A[i]
else
let j be the largest index of B such that B[j] < A[i]
B[j + 1] = A[i]
C[j + 1] = C[j]
INSERT(C[j + 1], A[i])
if j + 1 > L
L = L + 1
print C[L]