Longest common subsequence
In questo problema si vogliono cercare le massime sottosequenze comuni di massima lunghezza tra due sequenze e sapendo che:
Per esempio, .
Sottostruttura ottima
Di una sequenza lunga , un carattere può essere incluso o meno portando a alternative.
Si dice prefisso di la sequenza lunga per cui , e .
Data quindi una sequenza , la sottostruttura ottima diventa:
- Se , allora e
- Se , allora:
- Se , allora
- Se , allora
che riduce il problema a alternative, perchè per ogni prefisso degli se ne possono scegliere .
Ricorsione
La lunghezza delle sottosequenze massime appartenenti a si può trovare come:
Bottom-up
Sia una matrice contenete le lunghezze delle sottosequenze in , e una matrice contenente la miglior direzione presa dall'algoritmo partendo da :
- , se quindi si va a
- , se e si riduce a
- , se e si riduce a
LCS(X, Y)
m = X.length
n = Y.length
for i = 0 to m
c[i, 0] = 0
for j = 1 to n
c[0, j] = 0
for i = 1 to m
for j = 1 to n
if X[i] == Y[j]
c[i, j] = c[i-1, j-1] + 1
b[i, j] = ↖
else
if c[i-1, j] >= c[i, j-1]
c[i, j] = c[i-1, j]
b[i, j] = ↑
else
c[i, j] = c[i, j-1]
b[i, j] = ←
return b, c
La complessità è data dalla soluzione di tutti i sottoproblemi, quindi .
Si può quindi calcolare e visitare la soluzione con la stessa complessità di LCS
:
print_LCS(X, Y)
b, c = LCS(X, Y)
print_LCS_aux(X, b, X.length, Y.length)
print_LCS_aux(X, b, i, j)
if i > 0 and j > 0
if b[i, j] == ↖
print_LCS_aux(X, b, i-1, j-1)
print(X[i])
else
if b[i, j] == ↑
print_LCS_aux(X, b, i-1, j)
else
print_LCS_aux(X, b, i, j-1)
dove print_LCS_aux
ha complessità perchè nel caso peggiore deve decrementare sia che .
Per esempio, dati e le tabelle e ricavate saranno:
Ottimizzazione
Un'ottimizzazione consiste nel ridurre lo spazio consumato rimuovendo , perchè dipende dai vicini:
print_LCS_aux(X, c, i, j)
if i > 0 and j > 0
if c[i, j] == c[i-1, j]
print_LCS_aux(X, c, i-1, j)
else if c[i, j] == c[i, j-1]
print_LCS_aux(X, c, i, j-1)
else
print_LCS_aux(X, c, i-1, j-1)
print(X[i])
Dall'esempio precedente si nota che è importante l'ordine dei controlli, infatti se il vicino verso fosse controllato per primo, dalla cella si passerebbe a .
Inoltre con un vettore di elementi è anche possibile rimuovere se interessa solo la lunghezza.
Top-down
LCS(X, Y)
m = X.length
n = Y.length
c = []
for i = 1 to m*n
push(c, -1)
return LCS_aux(X, Y, c, m, n)
LCS_aux(X, Y, c, i, j)
if c[i, j] == -1
if i == 0 or j == 0
c[i, j] = 0
else
if X[i] == Y[j]
c[i, j] = LCS_aux(X, Y, c, i-1, j-1) + 1
else
c[i, j] = max(LCS_aux(X, Y, c, i-1, j), LCS_aux(X, Y, c, i, j-1))
return c[i, j]
La complessità di LCS_aux
è perchè al massimo riempie , mentre con LCS
diventa .