3. Динамично оптимиране

Най-дълга обща подредица [8.2.6]

Дадени са две редици (от числа или символи):

X = (x1, x2, ..., xm) и  Y = ( y1, y2, ..., yn)

Търси се най-дълга редица Z = (z1, z2, ..., zk), която е подредица на и Y едновременно. Z е подредица на  X, ако Z може да бъде получена чрез премахване на (0 или няколко) членове на X.

Най-напред ще търсим само дължината на най-дългата обща подредица. Ще приложим метода на динамичното оптимиране, като F(i, j) е търсената дължина за първите i члена на редицата X и първите j члена на редицата Y. Очевидно F(i,0) = 0 за всяко i и F(0, j) = 0 за всяко j. F(i, j) = F(i -1, j -1) + 1 за xi = yj, а F(i, j) = max {F(i -1, j), F(i, j -1)} в противен случай. Намираме последователно стойностите на F(i, j) и последната намерена стойност F(m,n) е решението на задачата.

Намирането на една най-дълга подредица става по същия начин, като тръгваме от последния елемент и следим откъде идва максималната стойност.

// lsc.c
#include <stdio.h>
#include <string.h>
#define MAXN 100
#define MAX(a,b) (((a)>(b)) ? (a) : (b))

char F[MAXN][MAXN];
const char x[MAXN] = "acbcacbcaba";
const char y[MAXN] = "abacacacababa";
unsigned m,n;
/*
"acb cacbcaba " "a cbcac bcaba"
"a bacacacababa" "abacacacab aba"
solution "a b cac caba " "a c cac b aba"
*/
unsigned lcs_len(void)
{ unsigned i,j;
for (i=0; i<=m; i++) F[i][0]= 0;
for (j=0; j<=n; j++) F[0][j]= 0;
for (i=1; i<=m; i++)
for (j=1; j<=n; j++)
if (x[i-1] == y[j-1]) F[i][j]=F[i-1][j-1]+1;
else F[i][j] = MAX(F[i-1][j], F[i][j-1]);
return F[m][n];
}
void print(unsigned i, unsigned j)
{ if (i == 0 || j == 0) return;
if (x[i-1] == y[j-1])
{ print(i-1,j-1);
printf("%c", x[i-1]);
}
else if (F[i][j] == F[i-1][j]) print(i-1,j);
else print(i,j-1);
}
int main()
{ m = strlen(x);
n = strlen(y);
printf("%u\n", lcs_len());
print(m,n);
return 0;
}
9
accacbab