Longest Common Subsequence (LCS) (original) (raw)

Given two strings, **s1 and **s2, find the length of the Longest Common Subsequence. If there is no **common subsequence, return 0. A **subsequence is a string generated from the original string by deleting 0 or more characters, without changing the relative order of the remaining characters.

For example, subsequences of "ABC" are "", "A", "B", "C", "AB", "AC", "BC" and "ABC". In general, a string of length **n has **2 n subsequences.

**Examples:

**Input: s1 = "ABC", s2 = "ACD"
**Output: 2
**Explanation: The longest subsequence which is present in both strings is "AC".

**Input: s1 = "AGGTAB", s2 = "GXTXAYB"
**Output: 4
**Explanation: The longest common subsequence is "GTAB".

**Input: s1 = "ABC", s2 = "CBA"
**Output: 1
**Explanation: There are three longest common subsequences of length 1, "A", "B" and "C".

Table of Content

[Naive Approach] Using Recursion - O(2 ^ min(m, n)) Time and O(min(m, n)) Space

The idea is to compare the last characters of s1 and s2. While comparing the strings s1 and s2 two cases arise:

**For example, consider the input strings s1 = "ABX" and s2 = "ACX".

LCS("ABX", "ACX") = 1 + LCS("AB", "AC") [Last Characters Match]

LCS("AB", "AC") = max( LCS("A", "AC") , LCS("AB", "A") ) [Last Characters Do Not Match]

LCS("A", "AC") = max( LCS("", "AC") , LCS("A", "A") ) = max(0, 1 + LCS("", "")) = 1

LCS("AB", "A") = max( LCS("A", "A") , LCS("AB", "") ) = max( 1 + LCS("", "", 0)) = 1

So overall result is1 + 1 **= 2

C++ `

#include
#include
#include using namespace std;

// Returns length of LCS for s1[0..m-1], s2[0..n-1] int lcsRec(string &s1, string &s2,int m,int n) {

// Base case: If either string is empty, the length of LCS is 0
if (m == 0 || n == 0)
    return 0;

// If the last characters of both substrings match
if (s1[m - 1] == s2[n - 1])
  
    // Include this character in LCS and recur for remaining substrings
    return 1 + lcsRec(s1, s2, m - 1, n - 1);

else
    // If the last characters do not match
    // Recur for two cases:
    // 1. Exclude the last character of s1 
    // 2. Exclude the last character of s2 
    // Take the maximum of these two recursive calls
    return max(lcsRec(s1, s2, m, n - 1), lcsRec(s1, s2, m - 1, n));

} int lcs(string &s1,string &s2){

int m = s1.size(), n = s2.size();
return lcsRec(s1,s2,m,n);

}

int main() { string s1 = "AGGTAB"; string s2 = "GXTXAYB"; int m = s1.size(); int n = s2.size();

cout << lcs(s1, s2) << endl;

return 0;

}

C

#include <stdio.h> #include <string.h>

int max(int x, int y) { return x > y ? x : y; }

// Returns length of LCS for s1[0..m-1], s2[0..n-1] int lcsRec(char s1[], char s2[], int m, int n) {

// Base case: If either string is empty, the length of LCS is 0
if (m == 0 || n == 0)
    return 0;

// If the last characters of both substrings match
if (s1[m - 1] == s2[n - 1])

    // Include this character in LCS and recur for remaining substrings
    return 1 + lcsRec(s1, s2, m - 1, n - 1);

else
    // If the last characters do not match
    // Recur for two cases:
    // 1. Exclude the last character of S1 
    // 2. Exclude the last character of S2 
    // Take the maximum of these two recursive calls
    return max(lcsRec(s1, s2, m, n - 1), lcsRec(s1, s2, m - 1, n));

} int lcs(char s1[],char s2[]){ int m = strlen(s1); int n = strlen(s2);

return lcsRec(s1,s2,m,n);

}

int main() { char s1[] = "AGGTAB"; char s2[] = "GXTXAYB"; printf("%d\n", lcs(s1, s2)); return 0; }

Java

class GfG {

// Returns length of LCS for s1[0..m-1], s2[0..n-1]
static int lcsRec(String s1, String s2, int m, int n) {

    // Base case: If either string is empty, the length of LCS is 0
    if (m == 0 || n == 0)
        return 0;

    // If the last characters of both substrings match
    if (s1.charAt(m - 1) == s2.charAt(n - 1))

        // Include this character in LCS and recur for remaining substrings
        return 1 + lcsRec(s1, s2, m - 1, n - 1);

    else
        // If the last characters do not match
        // Recur for two cases:
        // 1. Exclude the last character of S1 
        // 2. Exclude the last character of S2 
        // Take the maximum of these two recursive calls
        return Math.max(lcsRec(s1, s2, m, n - 1), lcsRec(s1, s2, m - 1, n));
}
static int lcs(String s1,String s2){
    int m = s1.length(), n = s2.length();
    return lcsRec(s1,s2,m,n);
}
public static void main(String[] args) {
    String s1 = "AGGTAB";
    String s2 = "GXTXAYB";
    System.out.println(lcs(s1, s2));
}

}

Python

def lcsRec(s1, s2, m, n):

# Base case: If either string is empty, the length of LCS is 0
if m == 0 or n == 0:
    return 0

# If the last characters of both substrings match
if s1[m - 1] == s2[n - 1]:

    # Include this character in LCS and recur for remaining substrings
    return 1 + lcsRec(s1, s2, m - 1, n - 1)

else:
    # If the last characters do not match
    # Recur for two cases:
    # 1. Exclude the last character of S1 
    # 2. Exclude the last character of S2 
    # Take the maximum of these two recursive calls
    return max(lcsRec(s1, s2, m, n - 1), lcsRec(s1, s2, m - 1, n))

def lcs(s1,s2): m = len(s1) n = len(s2) return lcsRec(s1,s2,m,n)

if name == "main": s1 = "AGGTAB" s2 = "GXTXAYB" print(lcs(s1, s2))

C#

using System;

class GfG {

// Returns length of LCS for s1[0..m-1], s2[0..n-1]
static int lcsRec(string s1, string s2, int m, int n) {

    // Base case: If either string is empty, the length of LCS is 0
    if (m == 0 || n == 0)
        return 0;

    // If the last characters of both substrings match
    if (s1[m - 1] == s2[n - 1])

        // Include this character in LCS and recur for remaining substrings
        return 1 + lcsRec(s1, s2, m - 1, n - 1);

    else
        // If the last characters do not match
        // Recur for two cases:
        // 1. Exclude the last character of S1 
        // 2. Exclude the last character of S2 
        // Take the maximum of these two recursive calls
        return Math.Max(lcsRec(s1, s2, m, n - 1), lcsRec(s1, s2, m - 1, n));
}
static int lcs(string s1,string s2){
    int m = s1.Length , n = s2.Length; 
    return lcsRec(s1,s2,m,n);
}
static void Main() {
    string s1 = "AGGTAB";
    string s2 = "GXTXAYB";
    Console.WriteLine(lcs(s1, s2));
}

}

JavaScript

function lcsRec(s1, s2, m, n) {

// Base case: If either string is empty, the length of LCS is 0
if (m === 0 || n === 0)
    return 0;

// If the last characters of both substrings match
if (s1[m - 1] === s2[n - 1])

    // Include this character in LCS and recur for remaining substrings
    return 1 + lcsRec(s1, s2, m - 1, n - 1);

else
    return Math.max(lcsRec(s1, s2, m, n - 1), lcsRec(s1, s2, m - 1, n));

} function lcs(s1,s2){

let m = s1.length;
let n = s2.length;
return lcsRec(s1,s2,m,n);

}

// driver code let s1 = "AGGTAB"; let s2 = "GXTXAYB"; let m = s1.length; let n = s2.length;

console.log(lcs(s1, s2, m, n));

`

[Better Approach] Using Memoization (Top Down DP) - O(m * n) Time and O(m * n) Space

If we use the above recursive approach for strings "**AXYT" and "**AYZX", we will get a partial recursion tree as shown below. Here we can see that the subproblem **L("AXY", "AYZ") is being calculated more than once. If the total tree is considered there will be several such overlapping subproblems. Hence we can optimize it either using memoization or tabulation.

Longest-Common-Subsequence

Overlapping Subproblems in Longest Common Subsequence

C++ `

#include
#include
#include
using namespace std;

// Returns length of LCS for s1[0..m-1], s2[0..n-1] int lcsRec(string &s1, string &s2, int m, int n, vector<vector> &memo) {

// Base Case
if (m == 0 || n == 0)
    return 0;

// Already exists in the memo table
if (memo[m][n] != -1)
    return memo[m][n];

// Match
if (s1[m - 1] == s2[n - 1])
    return memo[m][n] = 1 + lcsRec(s1, s2, m - 1, n - 1, memo);

// Do not match
return memo[m][n] = max(lcsRec(s1, s2, m, n - 1, memo), lcsRec(s1, s2, m - 1, n, memo));

} int lcs(string &s1,string &s2){ int m = s1.length(); int n = s2.length(); vector<vector> memo(m + 1, vector(n + 1, -1)); return lcsRec(s1, s2, m, n, memo); }

int main() { string s1 = "AGGTAB"; string s2 = "GXTXAYB"; cout << lcs(s1, s2) << endl; return 0; }

C

#include <stdio.h> #include <string.h>

// Define a maximum size for the strings #define MAX 1000

// Function to find the maximum of two integers int max(int a, int b) { return (a > b) ? a : b; }

// Returns length of LCS for s1[0..m-1], s2[0..n-1] int lcsRec(const char *s1, const char *s2, int m, int n, int memo[MAX][MAX]) {

// Base Case
if (m == 0 || n == 0) {
    return 0;
}

// Already exists in the memo table
if (memo[m][n] != -1) {
    return memo[m][n];
}

// Match
if (s1[m - 1] == s2[n - 1]) {
    return memo[m][n] = 1 + lcsRec(s1, s2, m - 1, n - 1, memo);
}

// Do not match
return memo[m][n] = max(lcsRec(s1, s2, m, n - 1, memo), lcsRec(s1, s2, m - 1, n, memo));

}

int lcs(char s1[],char s2[]){ int m = strlen(s1); int n = strlen(s2);

// Create memo table with fixed size
int memo[MAX][MAX];
for (int i = 0; i <= m; i++) {
    for (int j = 0; j <= n; j++) {
        // Initialize memo table with -1
        memo[i][j] = -1;
    }
}

return lcsRec(s1, s2, m, n, memo);

}

int main() { const char *s1 = "AGGTAB"; const char *s2 = "GXTXAYB";

printf("%d\n", lcs(s1, s2));

return 0;

}

Java

import java.util.Arrays;

class GfG {

// Returns length of LCS for s1[0..m-1], s2[0..n-1]
static int lcsRec(String s1, String s2, int m, int n,
               int[][] memo) {
    // Base Case
    if (m == 0 || n == 0)
        return 0;

    // Already exists in the memo table
    if (memo[m][n] != -1)
        return memo[m][n];

    // Match
    if (s1.charAt(m - 1) == s2.charAt(n - 1)) {
        return memo[m][n]
            = 1 + lcsRec(s1, s2, m - 1, n - 1, memo);
    }

    // Do not match
    return memo[m][n]
        = Math.max(lcsRec(s1, s2, m, n - 1, memo),
                   lcsRec(s1, s2, m - 1, n, memo));
}
static int lcs(String s1, String s2){
    int m = s1.length();
    int n = s2.length();
    int[][] memo = new int[m + 1][n + 1];

    // Initialize the memo table with -1
    for (int i = 0; i <= m; i++) {
        Arrays.fill(memo[i], -1);
    }

    return lcsRec(s1, s2, m, n, memo);
} 
public static void main(String[] args) {
    String s1 = "AGGTAB";
    String s2 = "GXTXAYB";

    System.out.println(lcs(s1, s2));
}

}

Python

def lcsRec(s1, s2, m, n, memo): # Base Case if m == 0 or n == 0: return 0

# Already exists in the memo table
if memo[m][n] != -1:
    return memo[m][n]

# Match
if s1[m - 1] == s2[n - 1]:
    memo[m][n] = 1 + lcsRec(s1, s2, m - 1, n - 1, memo)
    return memo[m][n]

# Do not match
memo[m][n] = max(lcsRec(s1, s2, m, n - 1, memo),
                 lcsRec(s1, s2, m - 1, n, memo))
return memo[m][n]

def lcs(s1, s2): m = len(s1) n = len(s2) memo = [[-1 for _ in range(n + 1)] for _ in range(m + 1)] return lcsRec(s1,s2,m,n,memo)

if name == "main": s1 = "AGGTAB" s2 = "GXTXAYB" print(lcs(s1, s2))

C#

using System; class GfG {

// Returns length of LCS for s1[0..m-1], s2[0..n-1]
static int lcsRec(string s1, string s2, int m,
                      int n, int[, ] memo) {
    // Base Case
    if (m == 0 || n == 0)
        return 0;

    // Already exists in the memo table
    if (memo[m, n] != -1)
        return memo[m, n];

    // Match
    if (s1[m - 1] == s2[n - 1]) {
        return memo[m, n]
            = 1 + lcsRec(s1, s2, m - 1, n - 1, memo);
    }

    // Do not match
    return memo[m, n]
        = Math.Max(lcsRec(s1, s2, m, n - 1, memo),
                   lcsRec(s1, s2, m - 1, n, memo));
}

static int lcs(string s1,string s2){
    int m = s1.Length;
    int n = s2.Length;
    
    int[, ] memo = new int[m + 1, n + 1];

    // Initialize memo array with -1
    for (int i = 0; i <= m; i++) {
        for (int j = 0; j <= n; j++) {
            memo[i, j] = -1;
        }
    }
    
    return lcsRec(s1,s2,m,n,memo);
}

public static void Main() {
    string s1 = "AGGTAB";
    string s2 = "GXTXAYB";
    
    Console.WriteLine(lcs(s1, s2));
}

}

JavaScript

function lcsRec(s1, s2, m, n, memo) { // Base Case if (m === 0 || n === 0) return 0;

// Already exists in the memo table
if (memo[m][n] !== -1)
    return memo[m][n];

// Match
if (s1[m - 1] === s2[n - 1]) {
    memo[m][n] = 1 + lcsRec(s1, s2, m - 1, n - 1, memo);
    return memo[m][n];
}

// Do not match
memo[m][n] = Math.max(lcsRec(s1, s2, m, n - 1, memo),
                      lcsRec(s1, s2, m - 1, n, memo));
return memo[m][n];

}

function lcs(s1, s2) {

const m = s1.length;
const n = s2.length;
const memo = Array.from({length : m + 1},
                        () => Array(n + 1).fill(-1));

return lcsRec(s1, s2, m, n, memo);

} // driver code const s1 = "AGGTAB"; const s2 = "GS1TS1AS2B";

console.log(lcs(s1, s2));

`

[Expected Approach 1] Using Bottom-Up DP (Tabulation) - O(m * n) Time and O(m * n) Space

There are two parameters that change in the recursive solution and these parameters go from 0 to m and 0 to n. So we create a 2D dp array of size (m+1) x (n+1).

Say the strings are **S1 = "AXTY" and **S2 = "AYZX", Follow below :

C++ `

#include #include using namespace std;

// Returns length of LCS for s1[0..m-1], s2[0..n-1] int lcs(string &s1, string &s2) { int m = s1.size(); int n = s2.size();

// Initializing a matrix of size (m+1)*(n+1)
vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0));

// Building dp[m+1][n+1] in bottom-up fashion
for (int i = 1; i <= m; ++i) {
    for (int j = 1; j <= n; ++j) {
        if (s1[i - 1] == s2[j - 1])
            dp[i][j] = dp[i - 1][j - 1] + 1;
        else
            dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
    }
}

// dp[m][n] contains length of LCS for s1[0..m-1]
// and s2[0..n-1]
return dp[m][n];

}

int main() { string s1 = "AGGTAB"; string s2 = "GXTXAYB"; cout << lcs(s1, s2) << endl;

return 0;

}

C

#include <stdio.h> #include <stdlib.h> #include <string.h>

int max(int x, int y);

// Function to find length of LCS for s1[0..m-1], s2[0..n-1] int lcs(const char *S1, const char *S2) { int m = strlen(S1); int n = strlen(S2);

// Initializing a matrix of size (m+1)*(n+1)
int dp[m + 1][n + 1];

// Building dp[m+1][n+1] in bottom-up fashion
for (int i = 0; i <= m; i++) {
    for (int j = 0; j <= n; j++) {
      
        if (i == 0 || j == 0)
            dp[i][j] = 0;
        
        else if (S1[i - 1] == S2[j - 1])
            dp[i][j] = dp[i - 1][j - 1] + 1;

        else
            dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
    }
}

return dp[m][n];

}

int max(int x, int y) { return (x > y) ? x : y; }

int main() { const char *S1 = "AGGTAB"; const char *S2 = "GXTXAYB"; printf("%d\n", lcs(S1, S2));

return 0;

}

Java

import java.util.Arrays;

class GfG {

// Returns length of LCS for s1[0..m-1], s2[0..n-1]
static int lcs(String S1, String S2) {
    int m = S1.length();
    int n = S2.length();

    // Initializing a matrix of size (m+1)*(n+1)
    int[][] dp = new int[m + 1][n + 1];

    // Building dp[m+1][n+1] in bottom-up fashion
    for (int i = 1; i <= m; i++) {
        for (int j = 1; j <= n; j++) {
            if (S1.charAt(i - 1) == S2.charAt(j - 1)) {
                dp[i][j] = dp[i - 1][j - 1] + 1;
            }
            else {
                dp[i][j] = Math.max(dp[i - 1][j],
                                    dp[i][j - 1]);
            }
        }
    }

    // dp[m][n] contains length of LCS for S1[0..m-1]
    // and S2[0..n-1]
    return dp[m][n];
}


public static void main(String[] args)
{
    String S1 = "AGGTAB";
    String S2 = "GXTXAYB";
    System.out.println( lcs(S1, S2));
}

}

Python

def lcs(S1, S2): m = len(S1) n = len(S2)

# Initializing a matrix of size (m+1)*(n+1)
dp = [[0] * (n + 1) for x in range(m + 1)]

# Building dp[m+1][n+1] in bottom-up fashion
for i in range(1, m + 1):
    for j in range(1, n + 1):
        if S1[i - 1] == S2[j - 1]:
            dp[i][j] = dp[i - 1][j - 1] + 1
        else:
            dp[i][j] = max(dp[i - 1][j],
                           dp[i][j - 1])

# dp[m][n] contains length of LCS for S1[0..m-1]
# and S2[0..n-1]
return dp[m][n]

if name == "main": S1 = "AGGTAB" S2 = "GXTXAYB" print(lcs(S1, S2))

C#

using System;

class GfG { // Returns length of LCS for S1[0..m-1], S2[0..n-1] static int lcs(string S1, string S2) { int m = S1.Length; int n = S2.Length;

    // Initializing a matrix of size (m+1)*(n+1)
    int[, ] dp = new int[m + 1, n + 1];

    // Building dp[m+1][n+1] in bottom-up fashion
    for (int i = 1; i <= m; i++) {
        for (int j = 1; j <= n; j++) {
            if (S1[i - 1] == S2[j - 1]) {
                dp[i, j] = dp[i - 1, j - 1] + 1;
            }
            else {
                dp[i, j] = Math.Max(dp[i - 1, j],
                                    dp[i, j - 1]);
            }
        }
    }

    // dp[m, n] contains length of LCS for S1[0..m-1]
    // and S2[0..n-1]
    return dp[m, n];
}

static void Main() {
    string S1 = "AGGTAB";
    string S2 = "GXTXAYB";
    Console.WriteLine(lcs(S1, S2));
}

}

JavaScript

function lcs(S1, S2) { const m = S1.length; const n = S2.length;

// Initializing a matrix of size (m+1)*(n+1)
const dp = Array.from({length : m + 1},
                      () => Array(n + 1).fill(0));

// Building dp[m+1][n+1] in bottom-up fashion
for (let i = 1; i <= m; i++) {
    for (let j = 1; j <= n; j++) {
        if (S1[i - 1] === S2[j - 1]) {
            dp[i][j] = dp[i - 1][j - 1] + 1;
        }
        else {
            dp[i][j]
                = Math.max(dp[i - 1][j], dp[i][j - 1]);
        }
    }
}

// dp[m][n] contains length of LCS for
// S1[0..m-1] and S2[0..n-1]
return dp[m][n];

}

const S1 = "AGGTAB"; const S2 = "GXTXAYB"; console.log(lcs(S1, S2));

`

[Expected Approach 2] Space Optimized - Two 1D Arrays - O(n * m) Time O(m) Space

One important observation in the above simple implementation is, in each iteration of the **outer loop we only need **values from all columns of the previous row. So there is no need to store **all rows in our dp matrix, we can just store two rows at a time and use them. In that way, used space will be reduced from **dp[m+1][n+1] to dp[2][n+1].

The **recurrence relation for the Longest Common Subsequence (LCS) problem is:

If the last character of **s1 and s2 match:

if the last characters of s1 and s2 **do not match, we take the maximum of two cases:
1. exclude the last character of s1
2. exclude the last char of s2

**Base Case: when the length of either s1 or s2 is 0, LCS is 0.

In the **recurrance relation one things that we can observe is for finding the current state **dp[i][j] we don't need to store the entire table, we only need to store the current row and the previous row because each value at **position (i, j) in the table only depends on:

Since only the previous row and the **current row are required to compute the LCS, we can reduce the space by using just two rows instead of the entire table. We use a **2D array of size 2 x (n+1) to store only two rows at a time.
We have used two array to store the previous and current row, **prev for **previous row and **curr for **current row, once the iteration for current row is done, we will set **prev = curr, so that curr row can serve as prev for next index.

C++ `

#include
#include
#include using namespace std;

int lcs(string& s1, string& s2) {

int n = s1.size();
int m = s2.size();

// Initialize two vectors to store the current 
  // and previous rows of the DP table
vector<int> prev(m + 1, 0), cur(m + 1, 0);

// Base case is covered as we have initialized 
  // the prev and cur vectors to 0.

for (int ind1 = 1; ind1 <= n; ind1++) {
    for (int ind2 = 1; ind2 <= m; ind2++) {
        if (s1[ind1 - 1] == s2[ind2 - 1]) {
          
            // Characters match, increment LCS length
            cur[ind2] = 1 + prev[ind2 - 1];
        }
        else
            // Characters don't match, consider the
              // maximum from above or left
            cur[ind2] = max(prev[ind2], cur[ind2 - 1]);
    }
  
    // Update the previous row with the current row
    prev = cur;
}

// Return the length of the Longest Common Subsequence
return prev[m];

} int main() {

string s1 = "AGGTAB";
string s2 = "GXTXAYB";
int res = lcs(s1, s2);
cout << res;
return 0;

}

Java

class GfG {

static int lcs(String s1, String s2) {
  
    int n = s1.length();
    int m = s2.length();

    // Create arrays to store the LCS lengths
    int prev[] = new int[m + 1];
    int cur[] = new int[m + 1];

    // Iterate through the strings and calculate LCS
    // lengths
    for (int ind1 = 1; ind1 <= n; ind1++) {
        for (int ind2 = 1; ind2 <= m; ind2++) {

            // If the characters at the current indices
            // are the same, increment the LCS length
            if (s1.charAt(ind1 - 1)
                == s2.charAt(ind2 - 1))
                cur[ind2] = 1 + prev[ind2 - 1];
          
            // If the characters are different, choose
            // the maximum LCS length by either
            // excluding a character in s1 or excluding
            // a character in s2
            else
                cur[ind2] = Math.max(prev[ind2],
                                     cur[ind2 - 1]);
        }
      
        // Update the 'prev' array to the values of
        // 'cur' for the next iteration
        prev = (int[])(cur.clone());
    }

    // Return the length of the Longest Common
    // Subsequence (LCS)
    return prev[m];
}

public static void main(String[] args) {

    String s1 = "AGGTAB";
    String s2 = "GXTXAYB";
    int res = lcs(s1, s2);
    System.out.println(res);
}

}

Python

def lcs(s1, s2): n = len(s1) m = len(s2)

# Initialize two arrays, 'prev' and 'cur',
# to store the DP values
prev = [0] * (m + 1)
cur = [0] * (m + 1)

# Loop through the characters of both strings
# to compute LCS
for ind1 in range(1, n + 1):
    for ind2 in range(1, m + 1):
        if s1[ind1 - 1] == s2[ind2 - 1]:
          
            # If the characters match, increment
            # LCS length by 1
            cur[ind2] = 1 + prev[ind2 - 1]
        else:
          
            # If the characters do not match, take 
            # the maximum of LCS
            # by excluding one character from s1 or s2
            cur[ind2] = max(prev[ind2], cur[ind2 - 1])

    # Update 'prev' to be the same as 'cur' for the 
    # next iteration
    prev = cur[:]

# The value in 'prev[m]' represents the length of the 
# Longest Common Subsequence
return prev[m]

if name == "main": s1 = "AGGTAB" s2 = "GXTXAYB" print(lcs(s1, s2))

C#

using System;

class GfG {

static int lcs(string s1, string s2) {
    int n = s1.Length;
    int m = s2.Length;

    // Initialize two arrays to store the current
    // and previous rows of the DP table
    int[] prev = new int[m + 1];
    int[] cur = new int[m + 1];

    // Base case is implicitly handled as the arrays are
    // initialized to 0

    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            if (s1[i - 1] == s2[j - 1]) {
              
                // Characters match, increment LCS
                // length
                cur[j] = 1 + prev[j - 1];
            }
            else {
              
                // Characters don't match, consider the
                // maximum from above or left
                cur[j] = Math.Max(prev[j], cur[j - 1]);
            }
        }

        // Update the previous row with
          // the current row
        Array.Copy(cur, prev, m + 1);
    }

    // Return the length of the Longest Common
    // Subsequence
    return prev[m];
}

static void Main() {

    string s1 = "AGGTAB";
    string s2 = "GXTXAYB";
    int res = lcs(s1, s2);
    Console.WriteLine(res);
}

}

JavaScript

function lcs(s1, s2) { const n = s1.length; const m = s2.length;

// Initialize arrays 'prev' and 'cur' to store dynamic
// programming results, both initialized with 0
const prev = new Array(m + 1).fill(0);
const cur = new Array(m + 1).fill(0);

// Base case is already covered as 'prev' and 'cur' are
// initialized to 0.

// Populating the 'cur' array using nested loops
for (let ind1 = 1; ind1 <= n; ind1++) {
    for (let ind2 = 1; ind2 <= m; ind2++) {
        if (s1[ind1 - 1] === s2[ind2 - 1]) {
            cur[ind2] = 1 + prev[ind2 - 1];
        }
        else {
            cur[ind2]
                = Math.max(prev[ind2], cur[ind2 - 1]);
        }
    }
    
    // Update 'prev' with the values of 'cur' for the
    // next iteration
    prev.splice(0, m + 1, ...cur);
}

// The result is stored in the last element of the
// 'prev' array
return prev[m];

}

const s1 = "AGGTAB"; const s2 = "GXTXAYB"; const res = lcs(s1, s2); console.log(res);

`

Time Complexity : O(n * m), where m is the length of string s1 and n is the length of string s2.
**Auxiliary Space: O(m), Only two 1D arrays are used, each of size m+1.

[Expected Approach 3] Further Space Optimized - Single Array - O(m*n) Time and O(n) Space

In this approach, the **auxiliary **space is further optimized by using a single DP array, where:
**dp[j] represents the value of **dp[i-1][j] (previous row's value) before updating. During the computation, **dp[j] is updated to represent the **current row value **dp[i][j]
Now the recurrance relations become:

C++ `

#include #include using namespace std;

int lcs(string &s1, string &s2) {

int m = s1.length(), n = s2.length();

// dp vector is initialized to all zeros
// This vector stores the LCS values for the current row.
// dp[j] represents LCS of s1[0..i] and s2[0..j]
vector<int> dp(n + 1, 0);

// i and j represent the lengths of s1 and s2 respectively
for (int i = 1; i <= m; ++i) {

    // prev stores the value from the previous
    // row and previous column (i-1), (j -1)
    // Used to keep track of LCS[i-1][j-1] while updating dp[j]
    int prev = dp[0];

    for (int j = 1; j <= n; ++j) {

        // temp temporarily stores the current
        // dp[j] before it gets updated
        int temp = dp[j];

        // If characters match, add 1 to the value
        // from the previous row and previous column
        // dp[j] = 1 + LCS[i-1][j-1]
        if (s1[i - 1] == s2[j - 1])
            dp[j] = 1 + prev;
        else
            // Otherwise, take the maximum of the
            // left (dp[j-1]) and top (dp[j]) values
            dp[j] = max(dp[j - 1], dp[j]);

        // Update prev for the next iteration
        // This keeps the value of the previous
      // row (i-1) for future comparisons
        prev = temp;
    }
}

// The last element of the vector contains the length of the LCS
// dp[n] stores the length of LCS of s1[0..m] and s2[0..n]
return dp[n];

}

int main() { string s1 = "AGGTAB", s2 = "GXTXAYB"; cout << lcs(s1, s2); return 0; }

Java

class GfG {

static int lcs(String s1, String s2) {
    int m = s1.length();
    int n = s2.length();
    
    // dp array is initialized to all zeros
    int[] dp = new int[n + 1];

    // i and j represent the lengths of s1 and s2 respectively
    for (int i = 1; i <= m; ++i) {
      
        // prev stores the value from the previous 
        // row and previous column (i-1), (j -1)
        int prev = dp[0];  
  
        for (int j = 1; j <= n; ++j) {
          
            // temp temporarily stores the current 
            // dp[j] before it gets updated
            int temp = dp[j];  
            if (s1.charAt(i - 1) == s2.charAt(j - 1)) {
              
                // If characters match, add 1 to the value 
                // from the previous row and previous column
                dp[j] = 1 + prev;  
            } else {
              
                // Otherwise, take the maximum of the 
                // left and top values
                dp[j] = Math.max(dp[j - 1], dp[j]);  
            }
          
            // Update prev for the next iteration
            prev = temp;  
        }
    }

    // The last element of the array contains 
      // the length of the LCS
    return dp[n];
}

public static void main(String[] args) {
    String s1 = "AGGTAB";
    String s2 = "GXTXAYB";
    int res = lcs(s1, s2);
    System.out.println(res);
}

}

Python

def lcs(s1, s2): m = len(s1) n = len(s2)

# dp array is initialized to all zeros
dp = [0] * (n + 1)

# i and j represent the lengths of s1 
# and s2 respectively
for i in range(1, m + 1):
  
    # prev stores the value from the previous 
    # row and previous column (i-1), (j -1)
    prev = dp[0]
  
    for j in range(1, n + 1):
      
        # temp temporarily stores the current 
        # dp[j] before it gets updated
        temp = dp[j]
        if s1[i - 1] == s2[j - 1]:
          
            # If characters match, add 1 to the value 
            # from the previous row and previous column
            dp[j] = 1 + prev
        else:
          
            # Otherwise, take the maximum of the 
            # left and top values
            dp[j] = max(dp[j - 1], dp[j])
            
        # Update prev for the next iteration
        prev = temp
  
# The last element of the list contains
# the length of the LCS
return dp[n]

if name == "main": s1 = "AGGTAB" s2 = "GXTXAYB" res = lcs(s1, s2) print(res)

C#

using System;

class GfG {

static int lcs(string s1, string s2) {
    int m = s1.Length;
    int n = s2.Length;
    
    // dp array is initialized to all zeros
    int[] dp = new int[n + 1];

    // i and j represent the lengths of 
   // s1 and s2 respectively
    for (int i = 1; i <= m; ++i) {
      
        // prev stores the value from the previous 
        // row and previous column (i-1), (j -1)
        int prev = dp[0];
  
        for (int j = 1; j <= n; ++j) {
          
            // temp temporarily stores the current 
            // dp[j] before it gets updated
            int temp = dp[j];
            if (s1[i - 1] == s2[j - 1]) {
              
                // If characters match, add 1 to the value 
                // from the previous row and previous column
                dp[j] = 1 + prev;
            } else {
              
                // Otherwise, take the maximum of the 
                // left and top values
                dp[j] = Math.Max(dp[j - 1], dp[j]);
            }
          
            // Update prev for the next iteration
            prev = temp;
        }
    }

    // The last element of the array 
      // contains the length of the LCS
    return dp[n];
}

static void Main() {
    string s1 = "AGGTAB";
    string s2 = "GXTXAYB";
    int res = lcs(s1, s2);
    Console.WriteLine(res);
}

}

JavaScript

function lcs(s1, s2) { const m = s1.length; const n = s2.length;

// dp array is initialized to all zeros
const dp = Array(n + 1).fill(0);

// i and j represent the lengths of s1 and s2
// respectively
for (let i = 1; i <= m; ++i) {

    // prev stores the value from the previous
    // row and previous column (i-1), (j -1)
    let prev = dp[0];

    for (let j = 1; j <= n; ++j) {

        // temp temporarily stores the current
        // dp[j] before it gets updated
        const temp = dp[j];
        if (s1[i - 1] === s2[j - 1]) {

            // If characters match, add 1 to the value
            // from the previous row and previous column
            dp[j] = 1 + prev;
        }
        else {

            // Otherwise, take the maximum of the
            // left and top values
            dp[j] = Math.max(dp[j - 1], dp[j]);
        }

        // Update prev for the next iteration
        prev = temp;
    }
}

// The last element of the array 
// contains the length of the LCS
return dp[n];

}

const s1 = "AGGTAB"; const s2 = "GXTXAYB"; const res = lcs(s1, s2); console.log(res);

`

Applications of LCS

Problems based on LCS