/* ---------------------------------- 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