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 2n 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 is 1 + 1 = 2
#include <iostream>
#include <string>
#include <algorithm>
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;
}
#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;
}
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));
}
}
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))
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));
}
}
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));
Output
4
[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.

#include <iostream>
#include <string>
#include <algorithm>
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<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] = 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<int>> memo(m + 1, vector<int>(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;
}
#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;
}
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));
}
}
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))
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));
}
}
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));
Output
4
[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 :
#include <iostream>
#include <vector>
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;
}
#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;
}
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));
}
}
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))
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));
}
}
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));
Output
4
[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.
#include <iostream>
#include <string>
#include <algorithm>
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;
}
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);
}
}
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))
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);
}
}
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);
Output
4
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].
#include <iostream>
#include <vector>
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;
}
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);
}
}
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)
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);
}
}
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);
Output
4
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