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
- [Better Approach] Using Memoization (Top Down DP) - O(m * n) Time and O(m * n) Space
- [Expected Approach 1] Using Bottom-Up DP (Tabulation) - O(m * n) Time and O(m * n) Space
- [Expected Approach 2] Space Optimized - Two 1D Arrays - O(n * m) Time O(m) Space
- [Expected Approach 3] Further Space Optimized - Single Array - O(m*n) Time and O(n) Space
[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:
- **Match : Make the recursion call for the remaining strings (strings of lengths m-1 and n-1) and add 1 to result.
- **Do not Match : Make two recursive calls. First for lengths m-1 and n, and second for m and n-1. Take the maximum of two results.
- **Base case : If any of the strings become empty, we return 0.
**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.
- 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 array of size (m+1) x (n+1).
- We initialize this array as -1 to indicate nothing is computed initially.
- Now we modify our recursive solution to first do a lookup in this table and if the value is -1, then only make recursive calls. This way we avoid re-computations of the same subproblems.

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).
- We first fill the known entries when m is 0 or n is 0.
- Then we fill the remaining entries using the recursive formula.
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:
- **dp[i][j] = 1 + dp[i-1][j-1]
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
- **dp[i][j] = max(dp[i-1][j],dp[i][j-1])
**Base Case: when the length of either s1 or s2 is 0, LCS is 0.
- **for i = 0 or j = 0 dp[i][j] = 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:
- The value directly **above it (dp[i-1][j]),
- The value directly to the **left (dp[i][j-1]),
- The value **diagonally left above it (dp[i-1][j-1]).
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:
- **if the characters s1[i-1] and s2[j-1] match, dp[j] = 1+ prev. Here, **prev is a temporary variable storing the diagonal value (dp[i-1][j-1]).
- If the characters don't match, dp[j] = max(dp[j-1], dp[j]). Here **dp[j] represents the value of **dp[i-1][j] before updating and dp[j-1] represents the value of dp[i-1][j].
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
- Used in diff utility to find differences between two data sources.
- Helps identify common parts and highlight changes between files.
- Widely used in version control systems (like Git) to track file changes.
- Detects additions, deletions, and modifications between different versions of files.
Problems based on LCS
- Printing LCS
- Edit Distance
- LCS of 3 Strings
- Longest Common Substring
- Count Distinct Subsequences
- Regular Expression Matching
- Longest Palindromic Substring
- Longest Repeated Subsequence
- Longest Palindromic Subsequence
- Shortest Common Supersequence
- Minimum Insertions and Deletions
- Minimum Insertions for Palindrome
- Print LCS of two Strings
- Dynamic Programming
- LCS and Edit Distance Dynamic
- Programming Practice Problems