LCS (Longest Common Subsequence) of three strings (original) (raw)
Given three strings s1, s2 and s3. Your task is to find the longest common sub-sequence in all three given sequences.
**Note: This problem is simply an extension of LCS.
**Examples:
**Input: s1 = "geeks" , s2 = "geeksfor", s3 = "geeksforgeeks"
**Output : 5
**Explanation: Longest common subsequence is "geeks" i.e., length = 5**Input: s1= "abcd1e2" , s2= "bc12ea" , s3= "bd1ea"
**Output: 3
**Explanation: Longest common subsequence is "b1e" i.e. length = 3.
Table of Content
- [Naive Approach] Using Recursion – O(3^(n1+n2+n3)) Time and O(min(n1, n2, n3)) Space
- [Better Approach 1] Using Top-Down DP (Memoization) – O(n1*n2*n3) Time and O(n1*n2*n3) Space
- [Better Apporach 2] Using Bottom-Up DP – O(n1*n2*n3) Time and O(n1*n2*n3) Space
- [Expected Approach] Space Optimized Bottom-Up DP – O(n1*n2*n3) Time and O(n2*n3) Space
[Naive Approach] Using Recursion – O(3^(n1+n2+n3)) Time and O(min(n1, n2, n3)) Space
The idea is to break the problem into **smaller subproblems and then combine the solutions to solve the original problem using recursion. We compare the last characters of the three strings, and there are two cases to consider:
- **When the last characters match: In this case, we reduce the problem by **recursively calling the function for smaller substrings, removing the last character from each string.
- **When the last characters do not match: Here, we need to consider the **maximum result from three different cases, each time excluding one character from one of the strings.
**Base Cases: If any of the strings s1, s2, or s3 has a length of 0, the Longest Common Subsequence (LCS) is 0.
**Recurrence Relation:
**If the last characters ofs1,s2, ands3are the same: These characters contribute to the LCS. Therefore, we add 1 to the LCS and call the recursive function with the previous characters of all three strings:
- LCSof3(n1, n2, n3) = 1 + LCSof3(n1-1, n2-1, n3-1)
**If the last characters do not match, we consider three possibilities:
- **Exclude the last character of s1 and compute the LCS for s1[0..n1-2], s2, and s3
**LCSof3(n1, n2, n3) = LCSof3(n1-1, n2, n3)- **Exclude the last character of s2 and compute the LCS for s1, s2[0..n2-2], and s3
**LCSof3(n1, n2, n3) = LCSof3(n1, n2-1, n3)- **Exclude the last character of s3 and compute the LCS for s1, s2, and s3[0..n3-2]
**LCSof3(n1, n2, n3) = LCSof3(n1, n2, n3-1)Thus, the final LCS is the maximum result obtained from these three cases:
- LCSof3(n1, n2, n3) = max(LCSof3(n1-1, n2 , n3), LCSof3(n1, n2-1, n3), LCSof3(n1, n2, n3-1))
C++ `
// C++ program to find the Longest Common Subsequence of // three string using recursion
#include <bits/stdc++.h> using namespace std; int findLCSOf3(string& s1, string& s2, string& s3, int n1, int n2, int n3) {
// Base case: If any of the strings is empty
if (n1 == 0 || n2 == 0 || n3 == 0)
return 0;
// If last characters of s1, s2, and s3 are the same
if (s1[n1 - 1] == s2[n2 - 1] && s2[n2 - 1] == s3[n3 - 1])
return 1 + findLCSOf3(s1, s2, s3, n1 - 1, n2 - 1, n3 - 1);
// If last characters are not the same, calculate
// LCS by excluding one string at a time
return max({
findLCSOf3(s1, s2, s3, n1 - 1, n2, n3),
findLCSOf3(s1, s2, s3, n1, n2 - 1, n3),
findLCSOf3(s1, s2, s3, n1, n2, n3 - 1)});}
int lcsOf3(string& s1, string& s2, string& s3) { int n1 = s1.size(); int n2 = s2.size(); int n3 = s3.size(); int res = findLCSOf3(s1, s2, s3, n1, n2, n3); return res; }
int main() { string s1 = "AGGT12"; string s2 = "12TXAYB"; string s3 = "12XBA"; int res = lcsOf3(s1, s2, s3); cout << res ; return 0; }
Java
// Java program to find the Longest Common Subsequence of // three string using recursion
class GfG { static int findLCSOf3(String s1, String s2, String s3, int n1, int n2, int n3) {
// Base case: If any of the strings is empty
if (n1 == 0 || n2 == 0 || n3 == 0)
return 0;
// If last characters of s1, s2, and s3 are the same
if (s1.charAt(n1 - 1) == s2.charAt(n2 - 1)
&& s2.charAt(n2 - 1) == s3.charAt(n3 - 1)) {
return 1
+ findLCSOf3(s1, s2, s3, n1 - 1, n2 - 1,
n3 - 1);
}
// If last characters are not the same, calculate
// LCS by excluding one string at a time
return Math.max(
Math.max(
findLCSOf3(s1, s2, s3, n1 - 1, n2, n3),
findLCSOf3(s1, s2, s3, n1, n2 - 1, n3)),
findLCSOf3(s1, s2, s3, n1, n2, n3 - 1));
}
static int lcsOf3(String s1, String s2, String s3) {
int n1 = s1.length();
int n2 = s2.length();
int n3 = s3.length();
return findLCSOf3(s1, s2, s3, n1, n2, n3);
}
public static void main(String[] args) {
String s1 = "AGGT12";
String s2 = "12TXAYB";
String s3 = "12XBA";
int res = lcsOf3(s1, s2, s3);
System.out.print(res);
}}
Python
Python program to find the Longest Common Subsequence of
three string using recursion
def findLCSOf3(s1, s2, s3, n1, n2, n3):
# Base case: If any of the strings is empty
if n1 == 0 or n2 == 0 or n3 == 0:
return 0
# If last characters of s1, s2, and s3 are the same
if s1[n1 - 1] == s2[n2 - 1] == s3[n3 - 1]:
return 1 + findLCSOf3(s1, s2, s3, n1 - 1, n2 - 1, n3 - 1)
# If last characters are not the same, calculate
# LCS by excluding one string at a time
return max(
findLCSOf3(s1, s2, s3, n1 - 1, n2, n3),
findLCSOf3(s1, s2, s3, n1, n2 - 1, n3),
findLCSOf3(s1, s2, s3, n1, n2, n3 - 1)
)def lcsOf3(s1, s2, s3): n1 = len(s1) n2 = len(s2) n3 = len(s3) return findLCSOf3(s1, s2, s3, n1, n2, n3)
if name == "main": s1 = "AGGT12" s2 = "12TXAYB" s3 = "12XBA" res = lcsOf3(s1, s2, s3) print(res)
C#
// C# program to find the Longest Common Subsequence of // three string
using System;
class GfG { static int findLCSOf3(string s1, string s2, string s3, int n1, int n2, int n3) {
// Base case: If any of the strings is empty
if (n1 == 0 || n2 == 0 || n3 == 0)
return 0;
// If last characters of s1, s2, and s3 are the same
if (s1[n1 - 1] == s2[n2 - 1]
&& s2[n2 - 1] == s3[n3 - 1])
return 1
+ findLCSOf3(s1, s2, s3, n1 - 1, n2 - 1,
n3 - 1);
// If last characters are not the same, calculate
// LCS by excluding one string at a time
return Math.Max(Math.Max(findLCSOf3(s1, s2, s3, n1 - 1, n2, n3),
findLCSOf3(s1, s2, s3, n1, n2 - 1, n3)),
findLCSOf3(s1, s2, s3, n1, n2, n3 - 1));
}
static int lcsOf3(string s1, string s2, string s3) {
int n1 = s1.Length;
int n2 = s2.Length;
int n3 = s3.Length;
return findLCSOf3(s1, s2, s3, n1, n2, n3);
}
static void Main() {
string s1 = "AGGT12";
string s2 = "12TXAYB";
string s3 = "12XBA";
int res = lcsOf3(s1, s2, s3);
Console.WriteLine(res);
}}
JavaScript
// JavaScript program to find the Longest Common Subsequence // of three string
function findLCSOf3(s1, s2, s3, n1, n2, n3) {
// Base case: If any of the strings is empty
if (n1 === 0 || n2 === 0 || n3 === 0) {
return 0;
}
// If last characters of s1, s2, and s3 are the same
if (s1[n1 - 1] === s2[n2 - 1]
&& s2[n2 - 1] === s3[n3 - 1]) {
return 1
+ findLCSOf3(s1, s2, s3, n1 - 1, n2 - 1,
n3 - 1);
}
// If last characters are not the same, calculate LCS by
// excluding one string at a time
return Math.max(
findLCSOf3(s1, s2, s3, n1 - 1, n2, n3),
Math.max(findLCSOf3(s1, s2, s3, n1, n2 - 1, n3),
findLCSOf3(s1, s2, s3, n1, n2, n3 - 1)));}
function lcsOf3(s1, s2, s3) { let n1 = s1.length; let n2 = s2.length; let n3 = s3.length; return findLCSOf3(s1, s2, s3, n1, n2, n3); }
// Driver Code let s1 = "AGGT12"; let s2 = "12TXAYB"; let s3 = "12XBA"; let res = lcsOf3(s1, s2, s3); console.log(res);
`
[Better Approach 1] Using Top-Down DP (Memoization****)** – O(n1*n2*n3) Time and O(n1*n2*n3) Space
If we notice carefully, we can observe that the above recursive solution holds the following two properties of Dynamic Programming.
**1. Optimal Substructure:
The solution to the **Longest Common Subsequence (LCS) of three strings can be derived from the optimal solutions of smaller subproblems. Specifically, for given strings s1, s2, and s3 with lengths n1, n2, and n3, we can express the recursive relation as follows:
- If the last characters of s1, s2, and s3 are the same:
LCSof3(n1, n2, n3) = 1+LCSof3(n1-1, n2-1, n3-1)- **If the last characters do not match, we consider three possibilities. the final LCS is the maximum result obtained from these three cases:
LCSof3(n1, n2, n3)=max(LCSof3(n1-1, n2, n3),LCSof3(n1, n2-1, n3), LCSof3(n1, n2, n3-1))**2. Overlapping Subproblems:
- When using a **recursive approach to solve the Longest Common Subsequence (LCS) problem for three strings, we observe that many subproblems are computed multiple times. For example, when calculating LCSof3(s1, s2, s3) for strings s1, s2, and s3 with lengths n1, n2, and n3, we may end up recomputing the LCS for the same combinations of string prefixes multiple times.
- The recursive****(previous)** solution involves changing **three parameters: the **current indices of the three strings (n1, n2, n3). We need to track these parameters, so we create a 3D array of size ****(n1+1) x (n2+1) x (n3+1)**, where n1, n2, and n3 represent the lengths of strings s1, s2, and s3. This array is used to store the results of subproblems for each combination of indices in the three strings.
- We initialize the 3D array with -1 to indicate that no subproblems have been computed yet.
- We check if the value at **memo[n1][n2][n3] is -1. If it is, we proceed to compute the result. otherwise, we return the stored result. C++ `
// C++ program to find the Longest Common Subsequence of // three string using memoization
#include <bits/stdc++.h> using namespace std;
int findLCSOf3(string& s1, string& s2, string& s3, int n1, int n2, int n3, vector<vector<vector>> &memo) {
// Base case: If any of the strings is empty
if (n1 == 0 || n2 == 0 || n3 == 0)
return 0;
if (memo[n1][n2][n3] != -1)
return memo[n1][n2][n3];
// If last characters of s1, s2, and s3 are the same
if (s1[n1 - 1] == s2[n2 - 1] && s2[n2 - 1] == s3[n3 - 1])
return memo[n1][n2][n3] = 1 + findLCSOf3(s1, s2,
s3, n1 - 1, n2 - 1, n3 - 1, memo);
// If last characters are not the same, calculate
// LCS by excluding one string at a time
return memo[n1][n2][n3] = max({findLCSOf3(s1, s2, s3, n1 - 1, n2, n3, memo),
findLCSOf3(s1, s2, s3, n1, n2 - 1, n3, memo),
findLCSOf3(s1, s2, s3, n1, n2, n3 - 1, memo)});}
int lcsOf3(string& s1, string& s2, string& s3) { int n1 = s1.size(); int n2 = s2.size(); int n3 = s3.size(); vector<vector<vector>> memo = vector<vector<vector>>(n1 + 1, vector<vector>(n2 + 1, vector(n3 + 1, -1))); int res = findLCSOf3(s1, s2, s3, n1, n2, n3, memo); return res; }
int main() { string s1 = "AGGT12"; string s2 = "12TXAYB"; string s3 = "12XBA"; int res = lcsOf3(s1, s2, s3); cout << res ;
return 0;}
Java
// Java program to find the Longest Common Subsequence of // three string using memoization
import java.util.*;
class GfG {
static int findLCSOf3(String s1, String s2, String s3, int n1, int n2, int n3,
int[][][] memo) {
// Base case: If any of the strings is empty
if (n1 == 0 || n2 == 0 || n3 == 0)
return 0;
// If the result is already computed, return it from
// the memo table
if (memo[n1][n2][n3] != -1)
return memo[n1][n2][n3];
// If the last characters of s1, s2, and s3 are the
// same
if (s1.charAt(n1 - 1) == s2.charAt(n2 - 1)
&& s2.charAt(n2 - 1) == s3.charAt(n3 - 1))
return memo[n1][n2][n3]
= 1 + findLCSOf3(s1, s2, s3, n1 - 1, n2 - 1, n3 - 1, memo);
// If last characters do not match, calculate LCS by
// excluding one string at a time
return memo[n1][n2][n3] = Math.max(
Math.max(findLCSOf3(s1, s2, s3, n1 - 1, n2, n3, memo),
findLCSOf3(s1, s2, s3, n1, n2 - 1, n3, memo)),
findLCSOf3(s1, s2, s3, n1, n2, n3 - 1, memo));
}
static int lcsOf3(String s1, String s2, String s3) {
int n1 = s1.length();
int n2 = s2.length();
int n3 = s3.length();
// Initialize the memo table with -1
int[][][] memo = new int[n1 + 1][n2 + 1][n3 + 1];
for (int i = 0; i <= n1; i++) {
for (int j = 0; j <= n2; j++) {
for (int k = 0; k <= n3; k++) {
memo[i][j][k] = -1;
}
}
}
return findLCSOf3(s1, s2, s3, n1, n2, n3, memo);
}
public static void main(String[] args) {
String s1 = "AGGT12";
String s2 = "12TXAYB";
String s3 = "12XBA";
int res = lcsOf3(s1, s2, s3);
System.out.print(res);
}}
Python
Python program to find the Longest Common Subsequence of
three string using memoization
def findLCSOf3(s1, s2, s3, n1, n2, n3, memo):
# Base case: If any of the strings is empty
if n1 == 0 or n2 == 0 or n3 == 0:
return 0
# If the result is already computed,
# return it from the memo table
if memo[n1][n2][n3] != -1:
return memo[n1][n2][n3]
# If the last characters of s1, s2, and s3 are the same
if s1[n1 - 1] == s2[n2 - 1] and s2[n2 - 1] == s3[n3 - 1]:
memo[n1][n2][n3] = 1 + \
findLCSOf3(s1, s2, s3, n1 - 1, n2 - 1, n3 - 1, memo)
return memo[n1][n2][n3]
# If last characters do not match,
# calculate LCS by excluding one string at a time
memo[n1][n2][n3] = max(
findLCSOf3(s1, s2, s3, n1 - 1, n2, n3, memo),
findLCSOf3(s1, s2, s3, n1, n2 - 1, n3, memo),
findLCSOf3(s1, s2, s3, n1, n2, n3 - 1, memo)
)
return memo[n1][n2][n3]def lcsOf3(s1, s2, s3): n1, n2, n3 = len(s1), len(s2), len(s3)
# Initialize the memoization table with -1
memo = [[[-1 for _ in range(n3 + 1)]
for _ in range(n2 + 1)] for _ in range(n1 + 1)]
# Call the recursive function
return findLCSOf3(s1, s2, s3, n1, n2, n3, memo)s1 = "AGGT12" s2 = "12TXAYB" s3 = "12XBA" res = lcsOf3(s1, s2, s3); print(res)
C#
// C# program to find the Longest Common Subsequence of // three string using memoization
using System;
class GfG {
static int findLCSOf3(string s1, string s2, string s3, int n1, int n2, int n3,
int[, , ] memo) {
// Base case: If any of the strings is empty
if (n1 == 0 || n2 == 0 || n3 == 0)
return 0;
// If the result is already computed, return it from
// the memo table
if (memo[n1, n2, n3] != -1)
return memo[n1, n2, n3];
// If the last characters of s1, s2, and s3 are the
// same
if (s1[n1 - 1] == s2[n2 - 1]
&& s2[n2 - 1] == s3[n3 - 1]) {
memo[n1, n2, n3]
= 1
+ findLCSOf3(s1, s2, s3, n1 - 1, n2 - 1,
n3 - 1, memo);
return memo[n1, n2, n3];
}
// If last characters do not match, calculate LCS by
// excluding one string at a time
memo[n1, n2, n3] = Math.Max(
Math.Max(findLCSOf3(s1, s2, s3, n1 - 1, n2, n3,memo),
findLCSOf3(s1, s2, s3, n1, n2 - 1, n3, memo)),
findLCSOf3(s1, s2, s3, n1, n2, n3 - 1, memo));
return memo[n1, n2, n3];
}
static int lcsOf3(string s1, string s2, string s3) {
int n1 = s1.Length;
int n2 = s2.Length;
int n3 = s3.Length;
// Initialize the memoization table with -1
int[, , ] memo = new int[n1 + 1, n2 + 1, n3 + 1];
for (int i = 0; i <= n1; i++) {
for (int j = 0; j <= n2; j++) {
for (int k = 0; k <= n3; k++) {
memo[i, j, k]
= -1;
}
}
}
return findLCSOf3(s1, s2, s3, n1, n2, n3, memo);
}
static void Main() {
string s1 = "AGGT12";
string s2 = "12TXAYB";
string s3 = "12XBA";
int res = lcsOf3(s1, s2, s3);
Console.WriteLine(res);
}}
JavaScript
// JavaScript program to find the Longest Common Subsequence of // three string using memoization
function findLCSOf3(s1, s2, s3, n1, n2, n3, memo) {
// Base case: If any of the strings is empty
if (n1 === 0 || n2 === 0 || n3 === 0) {
return 0;
}
// If the result is already computed, return it from the
// memo table
if (memo[n1][n2][n3] !== -1) {
return memo[n1][n2][n3];
}
// If the last characters of s1, s2, and s3 are the same
if (s1[n1 - 1] === s2[n2 - 1] && s2[n2 - 1] === s3[n3 - 1]) {
memo[n1][n2][n3]
= 1+ findLCSOf3(s1, s2, s3, n1 - 1, n2 - 1, n3 - 1, memo);
return memo[n1][n2][n3];
}
// If last characters do not match, calculate LCS by
// excluding one string at a time
memo[n1][n2][n3] = Math.max(
Math.max(
findLCSOf3(s1, s2, s3, n1 - 1, n2, n3, memo),
findLCSOf3(s1, s2, s3, n1, n2 - 1, n3, memo)),
findLCSOf3(s1, s2, s3, n1, n2, n3 - 1, memo));
return memo[n1][n2][n3];}
function lcsOf3(s1, s2, s3) { const n1 = s1.length; const n2 = s2.length; const n3 = s3.length;
// Initialize the memoization table with -1
const memo = Array.from(
{length : n1 + 1},
() => Array.from({length : n2 + 1},
() => Array(n3 + 1).fill(-1)));
// Call the recursive function
return findLCSOf3(s1, s2, s3, n1, n2, n3, memo);}
const s1 = "AGGT12"; const s2 = "12TXAYB"; const s3 = "12XBA"; const res = lcsOf3(s1, s2, s3); console.log(res);
`
[Better Apporach 2] Using Bottom-Up DP – O(n1*n2*n3) Time and O(n1*n2*n3) Space
The approach is similar to the **previous one. just instead of breaking down the problem recursively, we **iteratively build up the solution by calculating in bottom-up manner.
We create a 3D DP array of size ****(n1 + 1) * (n2 + 1) * (n3 + 1)** where each state dp[i][j][k] represents the length of the Longest Common Subsequence (LCS) of the first i characters of string s1, the first j characters of string s2, and the first k characters of string s3.
The dynamic programming relation for LCS of three strings is as follows:
**Base case: If any of the strings is empty (i == 0, j == 0, or k == 0), the LCS is 0:
**if i == 0 or j == 0 or k == 0 dp[i][j][k] = 0**Relation:
**If the last characters of s1, s2, and s3 are the same, they contribute to the LCS. We add 1 to the result of the subproblem that excludes the last character from each string:
if **s1[i-1] == s2[j-1] && s2[j-1] == s3[k-1] dp[i][j][k] = dp[i-1][j-1][k-1] + 1**If the last characters do not match, we need to check three cases:
- **Exclude the last character of s1: In this case, we compute the LCS by considering the first i-1 characters of s1, the first j characters of s2, and the first k characters of s3:
dp[i][j][k] = dp[i-1][j][k]- **Exclude the last character of s2: In this case, we compute the LCS by considering the first i characters of s1, the first j-1 characters of s2, and the first k characters of s3:
dp[i][j][k] = dp[i][j-1][k]- **Exclude the last character of s3: In this case, we compute the LCS by considering the first i characters of s1, the first j characters of s2, and the first k-1 characters of s3:
dp[i][j][k] = dp[i][j][k-1]The final result, **dp[n1][n2][n3], gives the length of the LCS of the three strings.
C++ `
// C++ program to find the Longest Common Subsequence of // three string using tabulation
#include <bits/stdc++.h> using namespace std;
int lcsOf3(string& s1, string& s2, string& s3) { int n1 = s1.length(); int n2 = s2.length(); int n3 = s3.length();
// Create a 3D array (dp) to store the // LCS lengths for each combination of substrings int dp[n1 + 1][n2 + 1][n3 + 1];
/* dp[i][j][k] contains length of LCS of
s1[0..i-1], s2[0..j-1] and s3[0..k-1] */
for (int i = 0; i <= n1; i++) {
for (int j = 0; j <= n2; j++) {
for (int k = 0; k <= n3; k++) {
if (i == 0 || j == 0 || k == 0)
dp[i][j][k] = 0;
else if (s1[i - 1] == s2[j - 1] && s1[i - 1] == s3[k - 1])
dp[i][j][k] = dp[i - 1][j - 1][k - 1] + 1;
else
dp[i][j][k] = max(max(dp[i - 1][j][k], dp[i][j - 1][k]),
dp[i][j][k - 1]);
}
}
}
return dp[n1][n2][n3];}
int main() { string s1 = "AGGT12"; string s2 = "12TXAYB"; string s3 = "12XBA"; int res = lcsOf3(s1, s2, s3); cout << res << endl;
return 0;}
Java
// Java program to find the Longest Common Subsequence of // three string using tabulation
class GfG {
static int lcsOf3(String s1, String s2, String s3) {
int n1 = s1.length();
int n2 = s2.length();
int n3 = s3.length();
// Create a 3D array (dp) to store the LCS lengths
// for each combination of substrings
int[][][] dp = new int[n1 + 1][n2 + 1][n3 + 1];
// dp[i][j][k] contains length of LCS of s1[0..i-1],
// s2[0..j-1], and s3[0..k-1]
for (int i = 0; i <= n1; i++) {
for (int j = 0; j <= n2; j++) {
for (int k = 0; k <= n3; k++) {
if (i == 0 || j == 0 || k == 0) {
// Base Case: any string is empty
dp[i][j][k] = 0;
}
else if (s1.charAt(i - 1) == s2.charAt(j - 1)
&& s1.charAt(i - 1) == s3.charAt(k - 1)) {
dp[i][j][k]= dp[i - 1][j - 1][k - 1]+ 1;
}
else {
dp[i][j][k] = Math.max(Math.max(dp[i - 1][j][k],
dp[i][j - 1][k]),dp[i][j][k - 1]);
}
}
}
}
// dp[n1][n2][n3] contains length of LCS for
// s1[0..n1-1], s2[0..n2-1], and s3[0..n3-1]
return dp[n1][n2][n3];
}
public static void main(String[] args) {
String s1 = "AGGT12";
String s2 = "12TXAYB";
String s3 = "12XBA";
int res = lcsOf3(s1, s2, s3);
System.out.print(res);
}}
Python
Python program to find the Longest Common Subsequence of
three string using tabulation
def lcsOf3(s1, s2, s3): n1 = len(s1) n2 = len(s2) n3 = len(s3)
# Create a 3D array (dp) to store
# the LCS lengths for each combination of substrings
dp = [[[0 for _ in range(n3 + 1)] for _ in range(n2 + 1)]
for _ in range(n1 + 1)]
# dp[i][j][k] contains length of LCS of s1[0..i-1],
# s2[0..j-1], and s3[0..k-1]
for i in range(n1 + 1):
for j in range(n2 + 1):
for k in range(n3 + 1):
if i == 0 or j == 0 or k == 0:
dp[i][j][k] = 0
elif s1[i - 1] == s2[j - 1] and s1[i - 1] == s3[k - 1]:
dp[i][j][k] = dp[i - 1][j - 1][k - 1] + \
1
else:
dp[i][j][k] = max(dp[i - 1][j][k], dp[i]
[j - 1][k], dp[i][j][k - 1])
# dp[n1][n2][n3] contains length of LCS for
# s1[0..n1-1], s2[0..n2-1], and s3[0..n3-1]
return dp[n1][n2][n3]if name == "main": s1 = "AGGT12" s2 = "12TXAYB" s3 = "12XBA" res = lcsOf3(s1, s2, s3) print(res)
C#
// C# program to find the Longest Common Subsequence of // three string using tabulation
using System;
class GfG {
static int lcsOf3(string s1, string s2, string s3) {
int n1 = s1.Length;
int n2 = s2.Length;
int n3 = s3.Length;
// Create a 3D array (dp) to store the LCS lengths
// for each combination of substrings
int[, , ] dp = new int[n1 + 1, n2 + 1, n3 + 1];
// dp[i, j, k] contains length of LCS of s1[0..i-1],
// s2[0..j-1], and s3[0..k-1]
for (int i = 0; i <= n1; i++) {
for (int j = 0; j <= n2; j++) {
for (int k = 0; k <= n3; k++) {
if (i == 0 || j == 0 || k == 0)
dp[i, j, k] = 0;
else if (s1[i - 1] == s2[j - 1]
&& s1[i - 1] == s3[k - 1])
dp[i, j, k]
= dp[i - 1, j - 1, k - 1]
+ 1;
else
dp[i, j, k] = Math.Max(
Math.Max(dp[i - 1, j, k],dp[i, j - 1, k]),
dp[i, j,k - 1]);
}
}
}
// dp[n1, n2, n3] contains the length of LCS for
// s1[0..n1-1], s2[0..n2-1], and s3[0..n3-1]
return dp[n1, n2, n3];
}
static void Main(string[] args) {
string s1 = "AGGT12";
string s2 = "12TXAYB";
string s3 = "12XBA";
int res = lcsOf3(s1, s2, s3);
Console.WriteLine(res);
}}
JavaScript
// JavaScript program to find the Longest Common Subsequence of // three string using tabulation
function lcsOf3(s1, s2, s3) { const n1 = s1.length; const n2 = s2.length; const n3 = s3.length;
// Create a 3D array (dp) to store the LCS lengths for
// each combination of substrings
let dp = Array.from(
{length : n1 + 1},
() => Array.from({length : n2 + 1},
() => Array(n3 + 1).fill(0)));
// dp[i][j][k] contains length of LCS of s1[0..i-1],
// s2[0..j-1], and s3[0..k-1]
for (let i = 0; i <= n1; i++) {
for (let j = 0; j <= n2; j++) {
for (let k = 0; k <= n3; k++) {
if (i === 0 || j === 0 || k === 0) {
// Base case: any string is empty
dp[i][j][k] = 0;
}
else if (s1[i - 1] === s2[j - 1]
&& s1[i - 1] === s3[k - 1]) {
dp[i][j][k] = dp[i - 1][j - 1][k - 1]
+ 1;
}
else {
dp[i][j][k] = Math.max(
Math.max(dp[i - 1][j][k],
dp[i][j - 1][k]),
dp[i][j][k - 1]);
}
}
}
}
// dp[n1][n2][n3] contains the length of LCS for
// s1[0..n1-1], s2[0..n2-1], and s3[0..n3-1]
return dp[n1][n2][n3];}
const s1 = "AGGT12"; const s2 = "12TXAYB"; const s3 = "12XBA"; const res = lcsOf3(s1, s2, s3); console.log(res);
`
[Expected Approach] Space Optimized Bottom-Up DP – O(n1*n2*n3) Time and O(n2*n3) Space
We observe that in the original 3D DP approach, at each step i, we only rely on values from the previous layer i-1. Hence, instead of maintaining the full 3D DP table dp[i][j][k], we can optimize space by using only two 2D arrays: prev and curr, each of size (n2+1) × (n3+1). These arrays are updated iteratively for each i.
**The transition state becomes:
**if (s1[i-1] == s2[j-1] && s2[j-1] == s3[k-1])
curr[j][k] = 1 + prev[j-1][k-1];**else
curr[j][k] = max({prev[j][k], curr[j-1][k], curr[j][k-1]});
After processing all j and k, we set prev = curr and repeat. This reduces the space complexity from **O(n1 × n2 × n3) to **O(n2 × n3).
C++ `
// C++ program to find the Longest Common Subsequence of // three strings using tabulation (space optimized)
#include <bits/stdc++.h> using namespace std;
// Function to compute LCS of three strings using 2D DP (space optimized) int lcsOf3(string &s1, string &s2, string &s3){
// Length of first string
int n1 = s1.length();
// Length of second string
int n2 = s2.length();
// Length of third string
int n3 = s3.length();
// Initialize two 2D arrays for DP:
// prev holds values for previous i-1 level
// curr holds values for current i level
vector<vector<int>> prev(n2 + 1, vector<int>(n3 + 1, 0));
vector<vector<int>> curr(n2 + 1, vector<int>(n3 + 1, 0));
// Iterate over all characters of s1
for (int i = 1; i <= n1; i++){
// Iterate over all characters of s2
for (int j = 1; j <= n2; j++){
// Iterate over all characters of s3
for (int k = 1; k <= n3; k++){
// If current characters of all three strings match
if (s1[i - 1] == s2[j - 1] && s2[j - 1] == s3[k - 1])
curr[j][k] = 1 + prev[j - 1][k - 1];
else
// Take the maximum of excluding current
// character from any one string
curr[j][k] = max({prev[j][k], curr[j - 1][k], curr[j][k - 1]});
}
}
// Move curr to prev for the next iteration
prev = curr;
}
// The result is in curr[n2][n3], which holds the final LCS length
return curr[n2][n3];}
int main(){
string s1 = "AGGT12";
string s2 = "12TXAYB";
string s3 = "12XBA";
// Compute and print the LCS of all three strings
int res = lcsOf3(s1, s2, s3);
cout << res << endl;
return 0;}
Java
class GfG {
// Function to compute LCS of three strings using space-optimized DP
static int lcsOf3(String s1, String s2, String s3) {
int n1 = s1.length();
int n2 = s2.length();
int n3 = s3.length();
// Create two 2D arrays for space-optimized DP
int[][] prev = new int[n2 + 1][n3 + 1];
int[][] curr = new int[n2 + 1][n3 + 1];
// Iterate over all characters in s1
for (int i = 1; i <= n1; i++) {
// Iterate over all characters in s2
for (int j = 1; j <= n2; j++) {
// Iterate over all characters in s3
for (int k = 1; k <= n3; k++) {
if (s1.charAt(i - 1) == s2.charAt(j - 1) &&
s2.charAt(j - 1) == s3.charAt(k - 1)) {
curr[j][k] = 1 + prev[j - 1][k - 1];
} else {
curr[j][k] = Math.max(prev[j][k],
Math.max(curr[j - 1][k], curr[j][k - 1]));
}
}
}
// Copy current to previous for the next round
for (int j = 0; j <= n2; j++) {
System.arraycopy(curr[j], 0, prev[j], 0, n3 + 1);
}
}
return curr[n2][n3];
}
public static void main(String[] args) {
String s1 = "AGGT12";
String s2 = "12TXAYB";
String s3 = "12XBA";
int result = lcsOf3(s1, s2, s3);
System.out.println(result);
}}
Python
def lcsOf3(s1, s2, s3):
# Lengths of the three strings
n1 = len(s1)
n2 = len(s2)
n3 = len(s3)
# Initialize two 2D DP arrays
# prev holds values for the previous i-1 level
# curr holds values for the current i level
prev = [[0] * (n3 + 1) for _ in range(n2 + 1)]
curr = [[0] * (n3 + 1) for _ in range(n2 + 1)]
# Iterate over all characters of s1
for i in range(1, n1 + 1):
# Iterate over all characters of s2
for j in range(1, n2 + 1):
# Iterate over all characters of s3
for k in range(1, n3 + 1):
# If characters match in all three strings
if s1[i - 1] == s2[j - 1] and s2[j - 1] == s3[k - 1]:
curr[j][k] = 1 + prev[j - 1][k - 1]
else:
# Take max by excluding current char from one of the strings
curr[j][k] = max(prev[j][k], curr[j - 1][k],
curr[j][k - 1])
# Copy curr to prev for the next i-level iteration
prev = [row[:] for row in curr]
# The final LCS length is at curr[n2][n3]
return curr[n2][n3]if name == "main": s1 = "AGGT12" s2 = "12TXAYB" s3 = "12XBA"
# Call the function and print result
res = lcsOf3(s1, s2, s3)
print(res)C#
using System;
class GfG{ // Function to compute LCS of three strings using space-optimized DP static int lcsOf3(string s1, string s2, string s3){
int n1 = s1.Length;
int n2 = s2.Length;
int n3 = s3.Length;
// Create two 2D arrays for space-optimized DP
int[,] prev = new int[n2 + 1, n3 + 1];
int[,] curr = new int[n2 + 1, n3 + 1];
// Iterate over all characters of s1
for (int i = 1; i <= n1; i++){
// Iterate over all characters of s2
for (int j = 1; j <= n2; j++){
// Iterate over all characters of s3
for (int k = 1; k <= n3; k++){
if (s1[i - 1] == s2[j - 1] && s2[j - 1] == s3[k - 1]){
curr[j, k] = 1 + prev[j - 1, k - 1];
}
else{
curr[j, k] = Math.Max(prev[j, k],
Math.Max(curr[j - 1, k], curr[j, k - 1]));
}
}
}
// Copy curr to prev for the next i
for (int j = 0; j <= n2; j++)
for (int k = 0; k <= n3; k++)
prev[j, k] = curr[j, k];
}
return curr[n2, n3];
}
static void Main() {
string s1 = "AGGT12";
string s2 = "12TXAYB";
string s3 = "12XBA";
int result = lcsOf3(s1, s2, s3);
Console.WriteLine(result);
}}
JavaScript
function lcsOf3(s1, s2, s3) { // Lengths of the three strings const n1 = s1.length; const n2 = s2.length; const n3 = s3.length;
// Initialize two 2D DP arrays:
// prev stores values from the previous i-1 iteration
// curr stores values for the current i iteration
let prev = Array.from({ length: n2 + 1 }, () => Array(n3 + 1).fill(0));
let curr = Array.from({ length: n2 + 1 }, () => Array(n3 + 1).fill(0));
// Iterate over all characters of s1
for (let i = 1; i <= n1; i++) {
// Iterate over all characters of s2
for (let j = 1; j <= n2; j++) {
// Iterate over all characters of s3
for (let k = 1; k <= n3; k++) {
// If characters match in all three strings
if (s1[i - 1] === s2[j - 1] && s2[j - 1] === s3[k - 1]) {
curr[j][k] = 1 + prev[j - 1][k - 1];
} else {
// Take max by excluding current char from one of the strings
curr[j][k] = Math.max(prev[j][k], curr[j - 1][k],
curr[j][k - 1]);
}
}
}
prev = curr.map(row => row.slice());
}
// The final result (LCS length) is at curr[n2][n3]
return curr[n2][n3];}
// Driver Code const s1 = "AGGT12"; const s2 = "12TXAYB"; const s3 = "12XBA";
// Call the function and log result const res = lcsOf3(s1, s2, s3); console.log(res);
`