/* ----------------------------------
Meaning of the input parameters
x = x0 x1 x2 .... x(i-1)
y = y0 y1 y2 .... y(j-1)
---------------------------------- */
int LCS( int i, int j, String x, String y )
{
int sol1, sol2, sol3;
/* ==============================
Base cases
============================== */
if ( i == 0 || j == 0 )
{
/* ------------------------------------------
One of the strings has 0 character
=> no match possible
Longest common subsequence = 0 characters
------------------------------------------- */
return(0);
}
if ( x[i-1] == y[i-1] )
{
sol1 = LCS(i-1, j-1, x, y);
return( sol1 + 1 );
}
else
{
sol2 = LCS(i-1, j, x, y);
sol3 = LCS(i, j-1, x, y);
if ( sol2 >= sol3 )
{
return(sol2);
}
else
{
return(sol3);
}
}
}
|
|
Example:
|
|
LCS with memoization:
/* ----------------------------------
Meaning of the input parameters
x = x0 x1 x2 .... x(i-1)
y = y0 y1 y2 .... y(j-1)
---------------------------------- */
int L[][]; // L[i][j] = Saved output of LCS(i,j) **********
int LCS( int i, int j, String x, String y )
{
int sol1, sol2, sol3;
if ( i == 0 || j == 0 )
{
/* ------------------------------------------
One of the strings has 0 character
=> no match possible
Longest common subsequence = 0 characters
------------------------------------------- */
return(0);
}
/* -------------------------------------------------
Check if we have a Memoized solution *********
------------------------------------------------- */
if ( L[i][j] >= 0 )
{
return( L[i][j] ); // Return stored solution !!!
}
/* ------------------------------------------------------
We will only run the recursive solver if we don't
have the solution LCS(i,j) stored...
------------------------------------------------------ */
if ( x[i-1] == y[i-1] )
{
sol1 = LCS(i-1, j-1, x, y);
L[i][j] = sol1 + 1; // Memoize the solution ********
return( sol1 + 1 );
}
else
{
sol2 = LCS(i-1, j, x, y);
sol3 = LCS(i, j-1, x, y);
if ( sol2 >= sol3 )
{
L[i][j] = sol2; // SAVE the solution ********
return(sol2);
}
else
{
L[i][j] = sol3; // SAVE the solution ********
return(sol3);
}
}
}
|
X = BAA Y = BBAB |
|
|
|
The standard dynamic programming technique looks like this:
for ( i = 0; i < m; i++ )
for ( j = 0; j < n; j++ )
L[i][j] = .... (uses L[i-..][j], L[i][j-..] or L[i-..][j-..])
|
|
|
fn = fn−1 + fn−2
f0 = 1
f1 = 1
|
Recursive solution:
int fib(int n)
{
int sol1, sol2, mySol;
/* =============================
Base cases
============================= */
if ( n == 0 )
{
return 1; // f0 = 1
}
else if ( n == 1 )
{
return 1; // f1 = 1
}
else /* Recursive solver */
{
sol1 = fib( n-1 ); // Solve smaller problem 1
sol2 = fib( n-2 ); // Solve smaller problem 2
mySol = sol1 + sol2; // Use solution to solve orig. proble,
return ( mySol ); // Claim credit...
}
}
|
How to run the program:
|
|
Inefficiency caused by:
|
int fib(int n)
{
int sol1, sol2, mySol;
/* =============================
Base cases
============================= */
if ( n == 0 )
{
return 1; // f0 = 1
}
else if ( n == 1 )
{
return 1; // f1 = 1
}
else /* Recursive solver */
{
if ( F[n] >= 0 )
return (F[n]); // Return solution if you have it
/* ========================
No solution: compute it
========================= */
sol1 = fib( n-1 ); // Solve smaller problem 1
sol2 = fib( n-2 ); // Solve smaller problem 2
mySol = sol1 + sol2; // Use solution to solve orig. proble,
F[n] = mySol; // Save it !
return ( mySol ); // Claim credit...
}
}
|
How to run the program:
|
|
public static int fib(int n)
{
int k;
/* =================
Base cases
================= */
F[0] = 1;
F[1] = 1;
/* ==========================================================
Compute direction: F[2], F[3], ..., F[n-2], F[n-1]. F[n]
========================================================== */
for ( k = 2; k <= n; k++ )
{
F[k] = F[k-1] + F[k-2]; // Dyn. prog
}
return ( F[n] );
}
|
How to run the program:
|
int L[][]; // L[i][j] = Saved output of LCS(i,j) **********
int LCS( int i, int j, String x, String y )
{
int sol1, sol2, sol3;
/* ========================================
Base cases
======================================== */
if ( i == 0 || j == 0 )
{
/* ------------------------------------------
One of the strings has 0 character
=> no match possible
Longest common subsequence = 0 characters
------------------------------------------- */
return(0); // I.e.: L[0][..] = 0 and L[..][0] = 0
}
/* -------------------------------------------------
Check if we have a Memoized solution *********
------------------------------------------------- */
if ( L[i][j] >= 0 )
{
return( L[i][j] ); // Return stored solution !!!
}
/* ------------------------------------------------------
We will only run the recursive solver if we don't
have the solution LCS(i,j) stored...
------------------------------------------------------ */
if ( x[i-1] == y[i-1] )
{
sol1 = LCS(i-1, j-1, x, y);
L[i][j] = sol1 + 1; // I.e.: L[i][j] = L[i-1][j-1] + 1
return( sol1 + 1 );
}
else
{
sol2 = LCS(i-1, j, x, y);
sol3 = LCS(i, j-1, x, y);
if ( sol2 >= sol3 )
{
L[i][j] = sol2; // sol2 = LCS(i-1, j, x, y)
return(sol2);
}
else
{
L[i][j] = sol3; // sol3 = LCS(i, j-1, x, y)
return(sol3);
}
// I.e.: L[i][j] = max( L[i-1][j], L[i][j-1] )
}
}
|
The direction of flow of data used in the computation is as follows:
|
Therefore, our loop index should run as follows:
for ( i = ..; i < m; i++ )
for ( j = ..; j < n; j++ )
L[i][j] = .... ; // Compute L[i][j]
|
So that when L[i][j] is computed, all the values of:
|
will be available !!!
for (i = 0; i < x.length()+1; i++)
L[i][0] = 0; // y = "" ===> LCS = 0
for (j = 0; j < y.length()+1; j++)
L[0][j] = 0; // x = "" ===> LCS = 0
|
if ( x[i-1] == y[j-1] )
{
L[i][j] = L[i-1][j-1] + 1;
}
else
{
if ( L[i-1][j] >= L[i][j-1] )
{
L[i][j] = L[i-1][j];
}
else
{
L[i][j] = L[i][j-1];
}
}
|
We must compute L[i][j] in this order:
for ( i = ..; i < m; i++ )
for ( j = ..; j < n; j++ )
L[i][j] = .... ; // Compute L[i][j]
|
public static int solveLCS(String x, String y)
{
int i, j;
/* ===============================================
Initialize the base cases
=============================================== */
for (i = 0; i < x.length()+1; i++)
L[i][0] = 0; // y = "" ===> LCS = 0
for (j = 0; j < y.length()+1; j++)
L[0][j] = 0; // x = "" ===> LCS = 0
/* =====================================================
Bottom-up (smaller to larger) computation of L[][j]
===================================================== */
for (i = 1; i < x.length()+1; i++)
{
for (j = 1; j < y.length()+1; j++)
{
if ( x.charAt(i-1) == y.charAt(j-1) )
{
L[i][j] = L[i-1][j-1] + 1;
}
else
{
if ( L[i-1][j] >= L[i][j-1] )
{
L[i][j] = L[i-1][j];
}
else
{
L[i][j] = L[i][j-1];
}
/* ===================================================
Note: we can replace the above if-statement with:
L[i][j] = max ( L[i-1][j] , L[i][j-1] );
=================================================== */
}
}
}
return( L[x.length()][y.length()] ); // This is LCS(x,y)
}
|
x = CACAB
y = BCA
Max length = 2
L[][]:
0 1 2 3
==================================
0 0 0 0 0
1 0 0 1 1
2 0 0 1 2
3 0 0 1 2
4 0 0 1 2
5 0 1 1 2
|
n = length of string x
m = length of string y