Open In App

Printing Longest Common Subsequence

Given two sequences, print the longest subsequence present in both of them.

Examples: 



We have discussed Longest Common Subsequence (LCS) problem in a previous post. The function discussed there was mainly to find the length of LCS. To find length of LCS, a 2D table L[][] was constructed. In this post, the function to construct and print LCS is discussed.

Following is detailed algorithm to print the LCS. It uses the same 2D table L[][].



  1. Construct L[m+1][n+1] using the steps discussed in previous post.
  2. The value L[m][n] contains length of LCS. Create a character array lcs[] of length equal to the length of lcs plus 1 (one extra to store \0).
  3. Traverse the 2D array starting from L[m][n]. Do following for every cell L[i][j] 
    • If characters (in X and Y) corresponding to L[i][j] are same (Or X[i-1] == Y[j-1]), then include this character as part of LCS. 
    • Else compare values of L[i-1][j] and L[i][j-1] and go in direction of greater value.

The following table (taken from Wiki) shows steps (highlighted) followed by the above algorithm.

  0 1 2 3 4 5 6 7
Ø M Z J A W X U
0 Ø 0 0 0 0 0 0 0 0
1 X 0 0 0 0 0 0 1 1
2 M 0 1 1 1 1 1 1 1
3 J 0 1 1 2 2 2 2 2
4 Y 0 1 1 2 2 2 2 2
5 A 0 1 1 2 3 3 3 3
6 U 0 1 1 2 3 3 3 4
7 Z 0 1 2 2 3 3 3 4

Following is the implementation of the above approach. 




/* Dynamic Programming implementation of LCS problem */
#include <cstdlib>
#include <cstring>
#include <iostream>
using namespace std;
 
/* Returns length of LCS for X[0..m-1], Y[0..n-1] */
void lcs(char* X, char* Y, int m, int n)
{
    int L[m + 1][n + 1];
 
    /* Following steps build L[m+1][n+1] in bottom up
      fashion. Note that L[i][j] contains length of LCS of
      X[0..i-1] and Y[0..j-1] */
    for (int i = 0; i <= m; i++) {
        for (int j = 0; j <= n; j++) {
            if (i == 0 || j == 0)
                L[i][j] = 0;
            else if (X[i - 1] == Y[j - 1])
                L[i][j] = L[i - 1][j - 1] + 1;
            else
                L[i][j] = max(L[i - 1][j], L[i][j - 1]);
        }
    }
 
    // Following code is used to print LCS
    int index = L[m][n];
 
    // Create a character array to store the lcs string
    char lcs[index + 1];
    lcs[index] = '\0'; // Set the terminating character
 
    // Start from the right-most-bottom-most corner and
    // one by one store characters in lcs[]
    int i = m, j = n;
    while (i > 0 && j > 0) {
        // If current character in X[] and Y are same, then
        // current character is part of LCS
        if (X[i - 1] == Y[j - 1]) {
            lcs[index - 1]
                = X[i - 1]; // Put current character in result
            i--;
            j--;
            index--; // reduce values of i, j and index
        }
 
        // If not same, then find the larger of two and
        // go in the direction of larger value
        else if (L[i - 1][j] > L[i][j - 1])
            i--;
        else
            j--;
    }
 
    // Print the lcs
    cout << "LCS of " << X << " and " << Y << " is " << lcs;
}
 
/* Driver program to test above function */
int main()
{
    char X[] = "AGGTAB";
    char Y[] = "GXTXAYB";
    int m = strlen(X);
    int n = strlen(Y);
    lcs(X, Y, m, n);
    return 0;
}




// Dynamic Programming implementation of LCS problem in Java
import java.io.*;
 
class LongestCommonSubsequence {
    // Returns length of LCS for X[0..m-1], Y[0..n-1]
    static void lcs(String X, String Y, int m, int n)
    {
        int[][] L = new int[m + 1][n + 1];
 
        // Following steps build L[m+1][n+1] in bottom up
        // fashion. Note that L[i][j] contains length of LCS
        // of X[0..i-1] and Y[0..j-1]
        for (int i = 0; i <= m; i++) {
            for (int j = 0; j <= n; j++) {
                if (i == 0 || j == 0)
                    L[i][j] = 0;
                else if (X.charAt(i - 1) == Y.charAt(j - 1))
                    L[i][j] = L[i - 1][j - 1] + 1;
                else
                    L[i][j] = Math.max(L[i - 1][j],
                                       L[i][j - 1]);
            }
        }
 
        // Following code is used to print LCS
        int index = L[m][n];
        int temp = index;
 
        // Create a character array to store the lcs string
        char[] lcs = new char[index + 1];
        lcs[index]
            = '\u0000'; // Set the terminating character
 
        // Start from the right-most-bottom-most corner and
        // one by one store characters in lcs[]
        int i = m;
        int j = n;
        while (i > 0 && j > 0) {
            // If current character in X[] and Y are same,
            // then current character is part of LCS
            if (X.charAt(i - 1) == Y.charAt(j - 1)) {
                // Put current character in result
                lcs[index - 1] = X.charAt(i - 1);
 
                // reduce values of i, j and index
                i--;
                j--;
                index--;
            }
 
            // If not same, then find the larger of two and
            // go in the direction of larger value
            else if (L[i - 1][j] > L[i][j - 1])
                i--;
            else
                j--;
        }
 
        // Print the lcs
        System.out.print("LCS of " + X + " and " + Y
                         + " is ");
        for (int k = 0; k <= temp; k++)
            System.out.print(lcs[k]);
    }
 
    // driver program
    public static void main(String[] args)
    {
        String X = "AGGTAB";
        String Y = "GXTXAYB";
        int m = X.length();
        int n = Y.length();
        lcs(X, Y, m, n);
    }
}
 
// Contributed by Pramod Kumar




# Dynamic programming implementation of LCS problem
 
# Returns length of LCS for X[0..m-1], Y[0..n-1]
 
 
def lcs(X, Y, m, n):
    L = [[0 for i in range(n+1)] for j in range(m+1)]
 
    # Following steps build L[m+1][n+1] in bottom up fashion. Note
    # that L[i][j] contains length of LCS of X[0..i-1] and Y[0..j-1]
    for i in range(m+1):
        for j in range(n+1):
            if i == 0 or j == 0:
                L[i][j] = 0
            elif X[i-1] == Y[j-1]:
                L[i][j] = L[i-1][j-1] + 1
            else:
                L[i][j] = max(L[i-1][j], L[i][j-1])
 
        # Create a string variable to store the lcs string
    lcs = ""
 
    # Start from the right-most-bottom-most corner and
    # one by one store characters in lcs[]
    i = m
    j = n
    while i > 0 and j > 0:
 
        # If current character in X[] and Y are same, then
        # current character is part of LCS
        if X[i-1] == Y[j-1]:
            lcs += X[i-1]
            i -= 1
            j -= 1
 
        # If not same, then find the larger of two and
        # go in the direction of larger value
        elif L[i-1][j] > L[i][j-1]:
            i -= 1
             
        else:
            j -= 1
 
    # We traversed the table in reverse order
    # LCS is the reverse of what we got
    lcs = lcs[::-1]
    print("LCS of " + X + " and " + Y + " is " + lcs)
 
 
# Driver program
X = "AGGTAB"
Y = "GXTXAYB"
m = len(X)
n = len(Y)
lcs(X, Y, m, n)
 
# This code is contributed by AMAN ASATI




// Dynamic Programming implementation
// of LCS problem in C#
using System;
 
class GFG {
    // Returns length of LCS for X[0..m-1], Y[0..n-1]
    static void lcs(String X, String Y, int m, int n)
    {
        int[, ] L = new int[m + 1, n + 1];
 
        // Following steps build L[m+1][n+1] in
        // bottom up fashion. Note that L[i][j]
        // contains length of LCS of X[0..i-1]
        // and Y[0..j-1]
        for (int i = 0; i <= m; i++) {
            for (int j = 0; j <= n; j++) {
                if (i == 0 || j == 0)
                    L[i, j] = 0;
                else if (X[i - 1] == Y[j - 1])
                    L[i, j] = L[i - 1, j - 1] + 1;
                else
                    L[i, j] = Math.Max(L[i - 1, j],
                                       L[i, j - 1]);
            }
        }
 
        // Following code is used to print LCS
        int index = L[m, n];
        int temp = index;
 
        // Create a character array
        // to store the lcs string
        char[] lcs = new char[index + 1];
 
        // Set the terminating character
        lcs[index] = '\0';
 
        // Start from the right-most-bottom-most corner
        // and one by one store characters in lcs[]
        int k = m, l = n;
        while (k > 0 && l > 0) {
            // If current character in X[] and Y
            // are same, then current character
            // is part of LCS
            if (X[k - 1] == Y[l - 1]) {
                // Put current character in result
                lcs[index - 1] = X[k - 1];
 
                // reduce values of i, j and index
                k--;
                l--;
                index--;
            }
 
            // If not same, then find the larger of two and
            // go in the direction of larger value
            else if (L[k - 1, l] > L[k, l - 1])
                k--;
            else
                l--;
        }
 
        // Print the lcs
        Console.Write("LCS of " + X + " and " + Y + " is ");
        for (int q = 0; q <= temp; q++)
            Console.Write(lcs[q]);
    }
 
    // Driver program
    public static void Main()
    {
        String X = "AGGTAB";
        String Y = "GXTXAYB";
        int m = X.Length;
        int n = Y.Length;
        lcs(X, Y, m, n);
    }
}
 
// This code is contributed by Sam007




<script>
function ReverseString(str) {
   return str.split('').reverse().join('')
}
   
function max(a, b)
{
    if (a > b)
        return a;
    else
        return b;
}
function printLCS(str1, str2) {
    var len1 = str1.length;
    var len2 = str2.length;
    var lcs = new Array(len1 + 1);
    for (var i = 0; i <= len1; i++) {
        lcs[i] = new Array(len2 + 1)
    }
    for (var i = 0; i <= len1; i++) {
        for (var j = 0; j <= len2; j++) {
            if (i == 0 || j == 0) {
                lcs[i][j] = 0;
            }
            else {
                if (str1[i - 1] == str2[j - 1]) {
                    lcs[i][j] = 1 + lcs[i - 1][j - 1];
                }
                else {
                    lcs[i][j] = max(lcs[i][j - 1], lcs[i - 1][j]);
                }
            }
        }}
        
        var n = lcs[len1][len2];
         document.write("Length of common subsequence is: " +
         n + "<br>" + "The subsequence is : ");
        var str="";
       var i = len1;
       var j = len2;
       while(i>0&&j>0)
       {
            if(str1[i - 1] == str2[j - 1])
            {
                str += str1[i - 1];
                i--;
                j--;
            }
            else{
            if(lcs[i][j-1]>lcs[i-1][j])
            {
                j--;
            }
            else
            {
                i--;
            }
            }
        }
       return ReverseString(str);
    }
    var str1 = "AGGTAB";
    var str2 = "GXTXAYB";
    document.write(printLCS(str1, str2));
     
    // This code is contributed by akshitsaxenaa09
</script>




<?php
// Dynamic Programming implementation of LCS problem
 
// Returns length of LCS for X[0..m-1], Y[0..n-1]
function lcs( $X, $Y, $m, $n )
{
    $L = array_fill(0, $m + 1,
         array_fill(0, $n + 1, NULL));
     
    /* Following steps build L[m+1][n+1] in bottom
       up fashion. Note that L[i][j] contains length
       of LCS of X[0..i-1] and Y[0..j-1] */
    for ($i = 0; $i <= $m; $i++)
    {
        for ($j = 0; $j <= $n; $j++)
        {
            if ($i == 0 || $j == 0)
                $L[$i][$j] = 0;
            else if ($X[$i - 1] == $Y[$j - 1])
                $L[$i][$j] = $L[$i - 1][$j - 1] + 1;
            else
                $L[$i][$j] = max($L[$i - 1][$j],
                                 $L[$i][$j - 1]);
        }
    }
     
    // Following code is used to print LCS
    $index = $L[$m][$n];
    $temp = $index;
     
    // Create a character array to store the lcs string
    $lcs = array_fill(0, $index + 1, NULL);
    $lcs[$index] = ''; // Set the terminating character
     
    // Start from the right-most-bottom-most corner
    // and one by one store characters in lcs[]
    $i = $m;
    $j = $n;
    while ($i > 0 && $j > 0)
    {
        // If current character in X[] and Y are same,
        // then current character is part of LCS
        if ($X[$i - 1] == $Y[$j - 1])
        {
            // Put current character in result
            $lcs[$index - 1] = $X[$i - 1];
            $i--;
            $j--;
            $index--;    // reduce values of i, j and index
        }
     
        // If not same, then find the larger of two
        // and go in the direction of larger value
        else if ($L[$i - 1][$j] > $L[$i][$j - 1])
            $i--;
        else
            $j--;
    }
     
    // Print the lcs
    echo "LCS of " . $X . " and " . $Y . " is ";
    for($k = 0; $k < $temp; $k++)
        echo $lcs[$k];
}
 
// Driver Code
$X = "AGGTAB";
$Y = "GXTXAYB";
$m = strlen($X);
$n = strlen($Y);
lcs($X, $Y, $m, $n);
 
// This code is contributed by ita_c
?>

Output
LCS of AGGTAB and GXTXAYB is GTAB

Time Complexity: O(m*n)
Auxiliary Space: O(m*n)

Top-down approach for printing Longest Common Subsequence:

Follow the steps below for the implementation:

Here is the implementation of the above recursive approach.




#include <iostream>
#include <map>
#include <string>
using namespace std;
using mpsss = map<pair<string, string>, string>;
using mpssi = map<pair<string, string>, int>;
using ss = pair<string, string>;
 
mpsss dp;
 
// For keep track of visited subproblem or not (0 = not
// visited, 1 = visited)
mpssi vs;
 
// utility function to reverse a string, we need it because
// our top-down approach return a reversed solution
string reverse(string str)
{
    string ans = str;
    int u = 0;
    int v = ans.length() - 1;
    while (u < v) {
        swap(ans[u], ans[v]);
        u++;
        v--;
    }
    return ans;
}
 
// utility function that compares two strings and return the
// longer in size.
string max_str(string a, string b)
{
    if (a.length() > b.length())
        return a;
    else
        return b;
}
 
string LCS_core(string a, string b)
{
 
    // size of string a
    int n_a = a.length();
 
    // size of string b
    int n_b = b.length();
 
    // Base case
    if (n_a == 0 || n_b == 0)
        return "";
 
    // dp index to access the dp structure
    ss dp_i = make_pair(a, b);
 
    // ans points to the memory location in the dp
    // structure in which the solution string will be stored
    string& ans = dp[dp_i];
 
    // if visited return solution from memory.
    if (vs[dp_i] == 1)
        return dp[dp_i];
 
    // if not visited, set the visit value to be one
    // (meaning its now visited)
    else
        vs[dp_i] = 1;
 
    // if the last two character match
    if (a[n_a - 1] == b[n_b - 1]) {
 
        // Add this character to the solution string
        ans += a[n_a - 1];
 
        // Erase last character from a
        a.erase(n_a - 1, 1);
 
        // Erase last character from b
        b.erase(n_b - 1, 1);
 
        // add to the solution string the value of
        // LCS_core(a, b) (the remaining strings after
        // deleting last characters)
        ans += LCS_core(a, b);
        return ans;
    }
 
    // Return longest string
    ans += max_str(LCS_core(a.substr(0, n_a - 1), b),
                   LCS_core(a, b.substr(0, n_b - 1)));
    return ans;
}
 
string LCS(string a, string b)
{
    ;
 
    // Reverse obtained result
    return reverse(LCS_core(a, b));
}
 
int main()
{
    string a = "AGGTAB";
    string b = "GXTXAYB";
    cout << LCS(a, b);
    return 0;
}




import java.util.HashMap;
import java.util.Map;
 
class Main {
    public static void main(String[] args) {
        String a = "AGGTAB";
        String b = "GXTXAYB";
        System.out.println(LCS(a, b));
    }
 
    private static Map<String, Map<String, String>> dp = new HashMap<>();
    private static Map<String, Map<String, Integer>> vs = new HashMap<>();
 
    private static String reverse(String str) {
        String ans = "";
        for (int i = str.length() - 1; i >= 0; i--) {
            ans += str.charAt(i);
        }
        return ans;
    }
 
    private static String max_str(String a, String b) {
        return a.length() > b.length() ? a : b;
    }
 
    private static String LCS_core(String a, String b) {
        if (a.isEmpty() || b.isEmpty()) {
            return "";
        }
 
        if (vs.containsKey(a) && vs.get(a).containsKey(b)) {
            return dp.get(a).get(b);
        }
 
        String ans = "";
        if (a.charAt(a.length() - 1) == b.charAt(b.length() - 1)) {
            ans += a.charAt(a.length() - 1);
            a = a.substring(0, a.length() - 1);
            b = b.substring(0, b.length() - 1);
            ans += LCS_core(a, b);
        } else {
            ans += max_str(LCS_core(a.substring(0, a.length() - 1), b),
                           LCS_core(a, b.substring(0, b.length() - 1)));
        }
        if (!dp.containsKey(a)) {
            dp.put(a, new HashMap<>());
        }
        if (!vs.containsKey(a)) {
            vs.put(a, new HashMap<>());
        }
        dp.get(a).put(b, ans);
        vs.get(a).put(b, 1);
        return ans;
    }
 
    private static String LCS(String a, String b) {
        return reverse(LCS_core(a, b));
    }
}
 
// This code is contributed by Susobhan Akhuli




from typing import Dict, Tuple
 
# Initialize an empty dictionary to store the solutions of subproblems
dp: Dict[Tuple[str, str], str] = {}
 
# Initialize an empty dictionary to keep track of visited subproblems
vs: Dict[Tuple[str, str], int] = {}
 
# Utility function to reverse a string, we need it because our top-down approach
# return a reversed solution
def reverse(s: str) -> str:
    ans = list(s)
    u, v = 0, len(ans) - 1
    while u < v:
        ans[u], ans[v] = ans[v], ans[u] #swap operation
        u += 1
        v -= 1
    return "".join(ans)
 
# Utility function that compares two strings and return the longer in size.
def max_str(a: str, b: str) -> str:
    return a if len(a) > len(b) else b
 
# Recursive function that takes two strings as input, and returns the LCS of them
def LCS_core(a: str, b: str) -> str:
    # Base case
    if not a or not b:
        return ""
    # dp index to access the dp structure
    dp_i = (a, b)
 
    # if visited return solution from memory
    if dp_i in vs:
        return dp[dp_i]
    else:
        vs[dp_i] = 1
 
    # if the last two character match
    if a[-1] == b[-1]:
        ans = a[-1] + LCS_core(a[:-1], b[:-1])
        dp[dp_i] = ans
        return ans
 
    # Return longest string
    ans = max_str(LCS_core(a[:-1], b), LCS_core(a, b[:-1]))
    dp[dp_i] = ans
    return ans
 
# Final wrapper function to call the recursive function and reverse the result
def LCS(a: str, b: str) -> str:
    return reverse(LCS_core(a, b))
 
a = "AGGTAB"
b = "GXTXAYB"
print(LCS(a, b))
 
# This code is contributed by Shivam Tiwari




using System;
using System.Collections.Generic
namespace LongestCommonSubsequence
{
 
public class GFG{
 
        static void Main(string[] args)
        {
            string a = "AGGTAB";
            string b = "GXTXAYB";
            Console.WriteLine(LCS(a, b));
        }
 
        // Utility function to reverse a string
        static string Reverse(string str)
        {
            char[] charArray = str.ToCharArray();
            Array.Reverse(charArray);
            return new string(charArray);
        }
 
        // Utility function to return the larger string
        static string MaxStr(string a, string b)
        {
            return a.Length > b.Length ? a : b;
        }
 
        // Main function that returns the LCS
        static string LCS_Core(string a, string b, Dictionary<string, string> dp, Dictionary<string, int> vs)
        {
            int n_a = a.Length;
            int n_b = b.Length;
 
            // Base case
            if (n_a == 0 || n_b == 0)
            {
                return "";
            }
 
            // dp index to access the dp dictionary
            string dp_i = a + "," + b;
 
            // ans points to the memory location in the dp
            // dictionary in which the solution string will be stored
            string ans;
            if (dp.TryGetValue(dp_i, out ans))
            {
                // If visited return solution from memory
                return ans;
            }
 
            // If not visited, set the visit value to be one
            // (meaning it's now visited)
            vs[dp_i] = 1;
 
            // If the last two characters match
            if (a[n_a - 1] == b[n_b - 1])
            {
                // Add this character to the solution string
                ans = a[n_a - 1] + LCS_Core(a.Substring(0, n_a - 1), b.Substring(0, n_b - 1), dp, vs);
                dp[dp_i] = ans;
                return ans;
            }
 
            // Return longest string
            ans = MaxStr(LCS_Core(a.Substring(0, n_a - 1), b, dp, vs), LCS_Core(a, b.Substring(0, n_b - 1), dp, vs));
            dp[dp_i] = ans;
            return ans;
        }
 
        static string LCS(string a, string b)
        {
            Dictionary<string, string> dp = new Dictionary<string, string>();
            Dictionary<string, int> vs = new Dictionary<string, int>();
 
            string ans = LCS_Core(a, b, dp, vs);
 
            // Reverse the obtained result
            return Reverse(ans);
        }
    }
}




// Initialize an empty dictionary to store the solutions of subproblems
let dp = {};
// Initialize an empty dictionary to keep track of visited subproblems
let vs = {};
 
// Utility function to reverse a string, we need it because our top-down approach
// return a reversed solution
function reverse(s) {
    let ans = s.split("");
    let u = 0;
    let v = ans.length - 1;
    while (u < v) {
        [ans[u], ans[v]] = [ans[v], ans[u]];   // swap operation
        u++;
        v--;
    }
    return ans.join("");
}
 
// Utility function that compares two strings and return the longer in size.
function maxStr(a, b) {
    return a.length > b.length ? a : b;
}
 
// Recursive function that takes two strings as input,
// and returns the LCS of them
function LCS_core(a, b) {
    // Base case
    if (!a || !b) return "";
     
    // dp index to access the dp structure
    let dp_i = `${a},${b}`;
     
    //  if visited return solution from memory
    if (dp_i in vs) return dp[dp_i];
    else vs[dp_i] = 1;
     
    // if the last two character match
    if (a[a.length - 1] === b[b.length - 1]) {
        let ans = a[a.length - 1] + LCS_core(a.slice(0, -1), b.slice(0, -1));
        dp[dp_i] = ans;
        return ans;
    }
     
    // Return longest string
    let ans = maxStr(LCS_core(a.slice(0, -1), b), LCS_core(a, b.slice(0, -1)));
    dp[dp_i] = ans;
    return ans;
}
 
// Final wrapper function to call the
// recursive function and reverse the result
function LCS(a, b) {
    return reverse(LCS_core(a, b));
}
 
// Driver Code
const a = "AGGTAB";
const b = "GXTXAYB";
console.log(LCS(a, b));

Output
GTAB

 Time complexity: O(m*n) where m is the length of the first string and n is the length of the second string. This is because, for each character in both strings, we need to check whether they are equal and then recursively call the LCS_core() function.

 Auxiliary space: O(m*n) as well. This is because we are using a map structure to keep track of the visited subproblems and the result of each subproblem. This structure is having a size of O(m*n).

 


Article Tags :