Lec4 DP
Lec4 DP
References:
Algorithms, Jeff Erickson, Chapter 3.
Algorithms, Gopal Pandurangan, Chapter 6.
Dynamic Programming 2/17
RecFib(n):
if n=0 return 0
if n=1 return 1
return RecFib(n-1) + RecFib(n-2)
Fibonacci Number 3/17
The recursive program has horrible time complexity. How bad? Let's
try to compute. Denote T (n) as the time complexity of computing
RecFib(n). Based on the recursion, we have the recurrence:
MemFib(n):
if n = 0 jj n = 1
return n
if F[n] is undefined
F[n] MemFib(n-1 )+MemFib(n-2 )
return F[n]
IterFib(n):
F[0] 0
F[1] 1
for i 1 to n
F[i] F[i-1 ] + F[i-2 ]
return F[n]
FastSplittable(A[1::n]):
SplitTable[n + 1] true
for i n down to 1
SplitTable[i] false
for j i to n
if IsWord(i ; j) and SplitTable[j + 1]
SplitTable[i] true
return SplitTable[1]
SnowWhiteandtheSevenDwarfsOnceuponatimeinagreat-
castleaPrincesdaughtergrewuphappyandcontentedinspiteofajeal-
ousstepmother . . .
L(i ; j) denotes the length of LIS of array A[j : : :n], with every
element bigger than A[i ].
8
>
<0 if j > n
L(i ; j) = L(i ; j + 1) if A[i] > A[j]
>
: max fL(i ; j + 1); 1 + L(j ; j + 1)g otherwise
Naive recursion algorithm has worst case time complexity of O(2n),
but note that we have only n2 distinct L(i ; j) to begin with.
We can fillup the L(i ; j) table in some order. What order do we
follow? We look at the dependencies. It seems L(i ; j) depends on
possibly L(i ; j + 1); L(j ; j + 1). To illustrate:
From the left figure, we immediately see that all the dependencies
are on the right. So if we fill the table from right to left, column by
column, we would satisfy all the dependencies.
To put it into algorithm:
FastLIS(A[1::n]):
A[0] ¡1
for i 0 to n
L[i ; n + 1] 0
for j n down to 1
for i 0 to j ¡ 1
keep 1 + L[j ; j + 1]
skip L[i ; j + 1]
if A[i] > A[j]
L[i ; j] skip
else
L[i ; j] max fkeep; skipg
return L[0; 1]
Let's try reducing the problem of L[i]. By definition, the LIS starts
with A[i]. What about the rest of the sequence? The rest must
starts with some A[j] (which is bigger than A[i]), and must be LIS
of A[j : : :n] (otherwise, A[i] + rest wouldn't be longest). So we have
the following recursion:
basically B[i] records the optimal j in the recursion. From B[i] we can
traceback the optimal solution corresponding to the length in L[1]:
Let's look at an example how this works.
i 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
A[i] 3 1 4 1 5 9 2 6 5 3 5 8 9 7 9
T[i]
B[i]
Edit Distance 11/17
F O O D
M O N E Y
Different letters in the same column means subst; gap in the first
word means insertion; gap in second means deletion.
It's easy to see in this case, no less than 4 steps are needed, so the
edit distance between FOOD and MONEY is 4.
In longer string pairs, it's not obvious what the edit distance is. For
example:
A L G O R I T H M
A L T R U I S T I C
This has 6 steps. Is this the minimum number of transformation
that is needed? It's hard to say. Let's come up with an algorithm
to tell us the edit distance (the minimum number of transformation
needed to between any two strings).
How to approach this problem (finding optimal solution)? Let's try
reduction, which means we take a small step and defer to a smaller
problem.
Suppose the gap representation (last page) represents the shortest
edit distance, then we have:
We want to compute E [m; n], for the given strings A[1:::m]; B[1:::n].
We consider the last column in the gap representation of the shortest
edit distance. There are three possibilities:
Insertion:
ALGORITHM
ALTRUISTI C
in which case: E [i ; j] = E [i ; j ¡ 1] + 1
Deletion
ALGORITH M
ALTRUISTIC
in which case E [i ; j] = E [i ¡ 1; j] + 1
Substitution:
ALGORITH M
ALTRUISTI C
in which case E [i ; j] = E [i ¡ 1; j ¡ 1] + 1
(or E [i ; j] = E [i ¡ 1; j ¡ 1]; if the last characters are the same)
The base cases are simple: E [0; j] = j ; E [i ; 0] = i.
To summarize the Edit function has the recurrence:
8
>
< E [i ; j ¡ 1] + 1
E [i ; j] = min E [i ; j ¡ 1] + 1
>
: E [i ¡ 1; j ¡ 1] + [A[i] =
/ B[j]]
Let's turn this recursion into dynamic program, using our mechanical
recipe.
Subproblem: each recursive subproblem is identified by two
indices 0 6 i 6 m; 0 6 j 6 n.
Memoization: we can put all possible E [i ; j] into a two dimen-
sional array E [0: : :m; 0: : :n]:
Dependencies & evaluation order:
Let's follow the mechanical recipe to turn this recursion into dynamic
program:
Subproblem:
Data structure:
Dependency & Evaluation order
Space and Time
Story: Text Compression for Bar Codes 13/17
j
!
_ _
M[i ; j ; X ] = M[i ; k ; Y ] M[k + 1; j ; Z ]
(X !YZ )2G i =k
Space complexity?
Time complexity?
Parsimonius Parserization. Programs often contain minor syntax
errors. Given context free grammar G and string S, find the min-
imal number of character substitutions to make S acceptable by
grammer G .
This seems quite hard. However, just by changing the definition of
the M[i ; j ; X ] a bit:
The recurrence?
j
N[i ; j ; X ] = min minN[i ; k ; Y ] + N[k + 1; j ; Z ]
(X !YZ )2G i =k
Regular Expression Matching 15/17
(Leetcode 10) Given an input string (s) and a pattern (p), implement
regular expression matching with support for '.' and '*' where:
'.' Matches any single character.
'*' Matches zero or more of the preceding element.
The matching should cover the entire input string (not partial).
E.g.
Input:
s = "aab", p = "c*a*b"
Output:
true
E x p l a n a t i o n : c can be repeated 0 times, a can be
repeated 1 time. Therefore, it matches "aab".
At this point, seeing the left-right structure we should immediately
think in terms of dynamic programming, and set out to find a recur-
rence. First try:
n¡1
X
P(n) = P(k) P(n ¡ k)
k =1
P(n) = (2n)
BestMatrixChainMulti(P[1::n]):
for k 1 to n ¡ 1
B1 BestMatrixChainMulti(P[1: : :k])
B2 BestMatrixChainMulti(P[k + 1: : :n])
Ck B1 + B2 + p0 pk pn
return the smallest Ck