Given two strings s1 and s2, return all distinct Longest Common Subsequences (LCS) in lexicographical order.
Examples:
Input: s1 = "abac", s2 = "aabca"
Output: ["aac", "aba", "abc"]
Explanation: Every subsequence that appears in both"abac"and"aabca"with the maximum possible length 3, are["aac" ,"aba", "abc"]Input: s1 = "baaa", s2 = "ca"
Output: ["a"]
Explanation: Every subsequence common to"baaa and"ca"of maximum length 1, is["a"].
Table of Content
[Naive Approach] Generate All Subsequences - O(2ⁿ × (m + n)) Time and O(2ⁿ) Space
Generate all subsequences of s1 using bitmask. Check each subsequence if it exists in s2 using two-pointer subsequence check. Track the maximum length and collect all distinct subsequences of that length.
// C++ program to find all distinct lcs
#include <iostream>
#include <vector>
#include <set>
using namespace std;
// check if t is a subsequence of s
bool isSubseq(string &t, string &s) {
int i = 0, j = 0;
while (i < t.size() && j < s.size()) {
if (t[i] == s[j]) ++i;
++j;
}
return i == t.size();
}
vector<string> allLCS(string &s1, string &s2) {
int n = s1.size();
int best = 0;
set<string> st;
// iterate over all 2^n subsequences of s1
for (int mask = 0; mask < (1<<n); ++mask) {
string sub;
for (int i = 0; i < n; ++i) {
if (mask & (1<<i))
sub.push_back(s1[i]);
}
int len = sub.size();
// too short, skip
if (len < best)
continue;
if (isSubseq(sub, s2)) {
// found a longer length
if (len > best) {
best = len;
st.clear();
}
st.insert(sub);
}
}
vector<string> ans;
// move into a sorted vector
for(auto i:st) ans.push_back(i);
return ans;
}
int main() {
string s1 = "abac";
string s2 = "aabca";
auto res = allLCS(s1, s2);
cout << "[";
for (int i = 0; i < res.size(); ++i) {
cout << "\"" << res[i] << "\"";
if (i+1 < res.size()) cout << ", ";
}
cout << "]\n";
return 0;
}
// Java program to find all distinct LCS
import java.util.*;
public class GfG {
// check if t is a subsequence of s
public static boolean isSubseq(String t, String s) {
int i = 0, j = 0;
while (i < t.length() && j < s.length()) {
if (t.charAt(i) == s.charAt(j)) ++i;
++j;
}
return i == t.length();
}
//
public static ArrayList<String> allLCS(String s1, String s2) {
int n = s1.length();
int best = 0;
Set<String> st = new HashSet<>();
// iterate over all 2^n subsequences of s1
for (int mask = 0; mask < (1 << n); ++mask) {
StringBuilder sub = new StringBuilder();
for (int i = 0; i < n; ++i) {
if ((mask & (1 << i)) != 0)
sub.append(s1.charAt(i));
}
int len = sub.length();
if (len < best)
continue;
if (isSubseq(sub.toString(), s2)) {
if (len > best) {
best = len;
st.clear();
}
st.add(sub.toString());
}
}
// build and sort
ArrayList<String> ans = new ArrayList<>(st);
Collections.sort(ans);
return ans;
}
public static void main(String[] args) {
String s1 = "abac", s2 = "aabca";
ArrayList<String> res = allLCS(s1, s2);
System.out.print("[");
for (int i = 0; i < res.size(); i++) {
System.out.print('"' + res.get(i) + '"');
if (i + 1 < res.size()) System.out.print(", ");
}
System.out.println("]");
}
}
# Python program to find all distinct LCS
from itertools import combinations
# check if t is a subsequence of s
def isSubseq(t, s):
i = 0
j = 0
while i < len(t) and j < len(s):
if t[i] == s[j]:
i += 1
j += 1
return i == len(t)
def allLCS(s1, s2):
n = len(s1)
best = 0
st = set()
# iterate over all 2^n subsequences of s1
for mask in range(1 << n):
sub = ''.join(s1[i] for i in range(n) if mask & (1 << i))
length = len(sub)
# too short, skip
if length < best:
continue
if isSubseq(sub, s2):
# found a longer length
if length > best:
best = length
st.clear()
st.add(sub)
ans = sorted(st)
return ans
if __name__ == '__main__':
s1 = "abac"
s2 = "aabca"
res = allLCS(s1, s2)
print(res)
// C# program to find all distinct LCS
using System;
using System.Collections.Generic;
using System.Linq;
class GfG {
// check if t is a subsequence of s
static bool isSubseq(string t, string s) {
int i = 0, j = 0;
while (i < t.Length && j < s.Length) {
if (t[i] == s[j]) ++i;
++j;
}
return i == t.Length;
}
static List<string> allLCS(string s1, string s2) {
int n = s1.Length;
int best = 0;
HashSet<string> st = new HashSet<string>();
// iterate over all 2^n subsequences of s1
for (int mask = 0; mask < (1 << n); ++mask) {
string sub = "";
for (int i = 0; i < n; ++i) {
if ((mask & (1 << i)) != 0)
sub += s1[i];
}
int len = sub.Length;
// too short, skip
if (len < best)
continue;
if (isSubseq(sub, s2)) {
// found a longer length
if (len > best) {
best = len;
st.Clear();
}
st.Add(sub);
}
}
List<string> ans = st.OrderBy(x => x).ToList();
return ans;
}
static void Main() {
string s1 = "abac", s2 = "aabca";
var res = allLCS(s1, s2);
Console.WriteLine("[" + string.Join(", ", res.Select(x => '"' + x + '"')) + "]");
}
}
// JavaScript program to find all distinct LCS
// check if t is a subsequence of s
function isSubseq(t, s) {
let i = 0, j = 0;
while (i < t.length && j < s.length) {
if (t[i] === s[j]) i++;
j++;
}
return i === t.length;
}
function allLCS(s1, s2) {
let n = s1.length;
let best = 0;
let st = new Set();
// iterate over all 2^n subsequences of s1
for (let mask = 0; mask < (1 << n); mask++) {
let sub = '';
for (let i = 0; i < n; i++) {
if (mask & (1 << i))
sub += s1[i];
}
let len = sub.length;
// too short, skip
if (len < best)
continue;
if (isSubseq(sub, s2)) {
// found a longer length
if (len > best) {
best = len;
st.clear();
}
st.add(sub);
}
}
let ans = Array.from(st).sort();
return ans;
}
const s1 = 'abac';
const s2 = 'aabca';
const res = allLCS(s1, s2);
console.log('[' + res.map(x => '"' + x + '"').join(', ') + ']');
Output
["aac", "aba", "abc"]
[Expected Approach] DP with Backtracking - O(m × n + L × 26 × (m + n)) Time and O(m × n) Space
Compute LCS length using DP table. Then backtrack to construct all distinct LCS strings by trying characters from 'a' to 'z' at each step, ensuring lexicographic order and only following paths that lead to full LCS length.
- Build DP table where dp[i][j] stores LCS length for s1[i..] and s2[j..]. The length of LCS is stored at dp[0][0]. Get lcsLen = dp[0][0]
- Backtrack from (0,0) with empty current string. If current length becomes lcsLen, add to result
- Try characters 'a' to 'z' in order. For each char, find next occurrence in s1 and s2 where dp[ii][jj] equals remaining length
- Recurse with ii+1, jj+1 after adding char
Illustration:
// C++ program to find all distinct lcs
#include <iostream>
#include <vector>
using namespace std;
// Compute length of LCS(s1, s2)
void lcsLength(string &s1, string &s2, vector<vector<int>> &dp) {
int m=s1.size(),n=s2.size();
for (int i = m - 1; i >= 0; --i) {
for (int j = n - 1; j >= 0; --j) {
if (s1[i] == s2[j])
dp[i][j] = dp[i + 1][j + 1] + 1;
else
dp[i][j] = max(dp[i + 1][j], dp[i][j + 1]);
}
}
// dp[0][0] contains length of LCS for s1[0..m-1]
// and s2[0..n-1]
}
// Backtrack to collect all LCS strings of total length lcsLen
void backtrack(string &s1, string &s2, int i, int j, int lcsLen,
vector<vector<int>> &dp, string &cur, vector<string> &res) {
if (cur.size() == lcsLen) {
res.push_back(cur);
return;
}
if (i == (int)s1.size() || j == (int)s2.size())
return;
int built = cur.size();
// Try each character in 'a'..'z' to enforce lex order
for (char ch = 'a'; ch <= 'z'; ++ch) {
bool found = false;
for (int ii = i; ii < (int)s1.size(); ++ii) {
if (s1[ii] != ch) continue;
for (int jj = j; jj < (int)s2.size(); ++jj) {
if (s2[jj] == ch
&& dp[ii][jj] == lcsLen - built)
{
cur.push_back(ch);
backtrack(s1, s2, ii+1, jj+1, lcsLen, dp, cur, res);
cur.pop_back();
found = true;
break;
}
}
if (found) break;
}
}
}
// Returns all distinct, lex-sorted LCS strings of s and t
vector<string> allLCS(string &s1, string &s2) {
int n = s1.size(), m = s2.size();
vector<vector<int>> dp(n+1, vector<int>(m+1));
// Find length of LCS
lcsLength(s1, s2, dp);
int lcsLen = dp[0][0];
// Backtrack to collect them
vector<string> res;
string cur;
backtrack(s1, s2, 0, 0, lcsLen, dp, cur, res);
return res;
}
int main() {
string s1 = "abac";
string s2 = "aabca";
vector<string> res = allLCS(s1, s2);
cout << "[";
for (int i = 0; i < res.size(); ++i) {
cout << "\"" << res[i] << "\"";
if (i + 1 < res.size()) cout << ", ";
}
cout << "]\n";
return 0;
}
// Java program to find all distinct LCS strings
import java.util.*;
class GfG {
// Compute LCS length table
static void lcsLength(String s1, String s2, int[][] dp) {
int n = s1.length();
int m = s2.length();
for (int i = n - 1; i >= 0; i--) {
for (int j = m - 1; j >= 0; j--) {
if (s1.charAt(i) == s2.charAt(j)) {
dp[i][j] = 1 + dp[i + 1][j + 1];
} else {
dp[i][j] = Math.max(dp[i + 1][j], dp[i][j + 1]);
}
}
}
}
// Backtrack to generate all LCS strings
static void backtrack(String s1, String s2, int i, int j,
int lcsLen, int[][] dp,
StringBuilder curr,
ArrayList<String> res) {
// One complete LCS is formed
if (curr.length() == lcsLen) {
res.add(curr.toString());
return;
}
if (i == s1.length() || j == s2.length()) {
return;
}
int built = curr.length();
// Try characters from a-z to maintain lexicographical order
for (char ch = 'a'; ch <= 'z'; ch++) {
boolean found = false;
for (int ii = i; ii < s1.length(); ii++) {
if (s1.charAt(ii) != ch)
continue;
for (int jj = j; jj < s2.length(); jj++) {
if (s2.charAt(jj) == ch &&
dp[ii][jj] == lcsLen - built) {
curr.append(ch);
backtrack(s1, s2,
ii + 1, jj + 1,
lcsLen, dp,
curr, res);
curr.deleteCharAt(curr.length() - 1);
found = true;
break;
}
}
if (found)
break;
}
}
}
// Returns all distinct LCS strings
static ArrayList<String> allLCS(String s1, String s2) {
int n = s1.length();
int m = s2.length();
int[][] dp = new int[n + 1][m + 1];
// Build LCS length table
lcsLength(s1, s2, dp);
int lcsLen = dp[0][0];
ArrayList<String> res = new ArrayList<>();
StringBuilder curr = new StringBuilder();
backtrack(s1, s2, 0, 0, lcsLen,
dp, curr, res);
return res;
}
public static void main(String[] args) {
String s1 = "abac";
String s2 = "aabca";
ArrayList<String> res = allLCS(s1, s2);
System.out.print("[");
for (int i = 0; i < res.size(); i++) {
System.out.print("\"" + res.get(i) + "\"");
if (i != res.size() - 1)
System.out.print(", ");
}
System.out.println("]");
}
}
# Python program to find all distinct LCS strings
# Compute length of LCS(s1, s2)
def lcsLength(s1, s2, dp):
m, n = len(s1), len(s2)
for i in range(m - 1, -1, -1):
for j in range(n - 1, -1, -1):
if s1[i] == s2[j]:
dp[i][j] = dp[i + 1][j + 1] + 1
else:
dp[i][j] = max(dp[i + 1][j], dp[i][j + 1])
# Backtrack to collect all LCS strings of total length lcsLen
def backtrack(s1, s2, i, j, lcsLen, dp, cur, res):
if len(cur) == lcsLen:
res.append(''.join(cur))
return
if i == len(s1) or j == len(s2):
return
built = len(cur)
# Try each character in 'a'..'z' to enforce lex order
for ch in range(ord('a'), ord('z') + 1):
char = chr(ch)
found = False
for ii in range(i, len(s1)):
if s1[ii] != char:
continue
for jj in range(j, len(s2)):
if s2[jj] == char and dp[ii][jj] == lcsLen - built:
cur.append(char)
backtrack(s1, s2, ii + 1, jj + 1, lcsLen, dp, cur, res)
cur.pop()
found = True
break
if found:
break
# Returns all distinct, lex-sorted LCS strings of s and t
def allLCS(s1, s2):
n, m = len(s1), len(s2)
dp = [[0] * (m + 1) for _ in range(n + 1)]
# Find length of LCS
lcsLength(s1, s2, dp)
lcsLen = dp[0][0]
# Backtrack to collect them
res = []
cur = []
backtrack(s1, s2, 0, 0, lcsLen, dp, cur, res)
return res
# Driver code
if __name__ == "__main__":
s1 = "abac"
s2 = "aabca"
res = allLCS(s1, s2)
print("[", end="")
for i in range(len(res)):
print(f"\"{res[i]}\"", end="")
if i + 1 < len(res):
print(", ", end="")
print("]")
// C# program to find all distinct LCS strings
using System;
using System.Collections.Generic;
using System.Text;
class GfG {
// Compute length of LCS(s1, s2)
static void lcsLength(string s1, string s2, int[,] dp) {
int m = s1.Length, n = s2.Length;
for (int i = m - 1; i >= 0; i--) {
for (int j = n - 1; j >= 0; j--) {
if (s1[i] == s2[j])
dp[i, j] = dp[i + 1, j + 1] + 1;
else
dp[i, j] = Math.Max(dp[i + 1, j], dp[i, j + 1]);
}
}
}
// Backtrack to collect all LCS strings of total length lcsLen
static void backtrack(string s1, string s2, int i, int j, int lcsLen,
int[,] dp, StringBuilder cur, List<string> res) {
if (cur.Length == lcsLen) {
res.Add(cur.ToString());
return;
}
if (i == s1.Length || j == s2.Length)
return;
int built = cur.Length;
// Try each character in 'a'..'z' to enforce lex order
for (char ch = 'a'; ch <= 'z'; ch++) {
bool found = false;
for (int ii = i; ii < s1.Length; ii++) {
if (s1[ii] != ch) continue;
for (int jj = j; jj < s2.Length; jj++) {
if (s2[jj] == ch && dp[ii, jj] == lcsLen - built) {
cur.Append(ch);
backtrack(s1, s2, ii + 1, jj + 1, lcsLen, dp, cur, res);
cur.Remove(cur.Length - 1, 1);
found = true;
break;
}
}
if (found) break;
}
}
}
// Returns all distinct, lex-sorted LCS strings of s and t
static List<string> allLCS(string s1, string s2) {
int n = s1.Length, m = s2.Length;
int[,] dp = new int[n + 1, m + 1];
// Find length of LCS
lcsLength(s1, s2, dp);
int lcsLen = dp[0, 0];
// Backtrack to collect them
List<string> res = new List<string>();
StringBuilder cur = new StringBuilder();
backtrack(s1, s2, 0, 0, lcsLen, dp, cur, res);
return res;
}
static void Main(string[] args) {
string s1 = "abac";
string s2 = "aabca";
List<string> res = allLCS(s1, s2);
Console.Write("[");
for (int i = 0; i < res.Count; i++) {
Console.Write("\"" + res[i] + "\"");
if (i + 1 < res.Count) Console.Write(", ");
}
Console.WriteLine("]");
}
}
// JavaScript program to find all distinct LCS strings
// Compute length of LCS(s1, s2)
function lcsLength(s1, s2, dp) {
const m = s1.length, n = s2.length;
for (let i = m - 1; i >= 0; i--) {
for (let j = n - 1; j >= 0; j--) {
if (s1[i] === s2[j])
dp[i][j] = dp[i + 1][j + 1] + 1;
else
dp[i][j] = Math.max(dp[i + 1][j], dp[i][j + 1]);
}
}
}
// Backtrack to collect all LCS strings of total length lcsLen
function backtrack(s1, s2, i, j, lcsLen, dp, cur, res) {
if (cur.length === lcsLen) {
res.push(cur.join(''));
return;
}
if (i === s1.length || j === s2.length)
return;
const built = cur.length;
// Try each character in 'a'..'z' to enforce lex order
for (let chCode = 97; chCode <= 122; chCode++) {
const ch = String.fromCharCode(chCode);
let found = false;
for (let ii = i; ii < s1.length; ii++) {
if (s1[ii] !== ch) continue;
for (let jj = j; jj < s2.length; jj++) {
if (s2[jj] === ch && dp[ii][jj] === lcsLen - built) {
cur.push(ch);
backtrack(s1, s2, ii + 1, jj + 1, lcsLen, dp, cur, res);
cur.pop();
found = true;
break;
}
}
if (found) break;
}
}
}
// Returns all distinct, lex-sorted LCS strings of s and t
function allLCS(s1, s2) {
const n = s1.length, m = s2.length;
const dp = Array(n + 1);
for (let i = 0; i <= n; i++) {
dp[i] = Array(m + 1).fill(0);
}
// Find length of LCS
lcsLength(s1, s2, dp);
const lcsLen = dp[0][0];
// Backtrack to collect them
const res = [];
const cur = [];
backtrack(s1, s2, 0, 0, lcsLen, dp, cur, res);
return res;
}
// Driver code
const s1 = "abac";
const s2 = "aabca";
const res = allLCS(s1, s2);
console.log(JSON.stringify(res));
Output
["aac", "aba", "abc"]