DSA
Print all longest Common subsequence
Dynamic Programming. Time O(m + n).
Practice Link
You are given two strings s and t. Now your task is to print all longest common sub-sequences in lexicographical order.
Approach#
First build the standard LCS DP table in O(m*n). Then backtrack from (m, n) to (0, 0): when characters match, include the character and move diagonally; when they don't, branch in both directions (up or left) if the LCS value is the same from either. A memo keyed by (i, j, currentString) prevents re-exploring the same state with the same partial result. All complete paths (when i == 0 or j == 0) are reversed and stored in a set to deduplicate. The set's sorted order automatically gives lexicographical output.
class Solution {
public:
void findAllLcs(string &s, string &t, int i, int j, vector<vector<int>> &dp, string curr, set<string> &res, map<string, bool>&memo)
{
//backtrack and find all LCS
if(i==0 || j==0)
{
reverse(curr.begin(), curr.end());
res.insert(curr);
return;
}
string key = to_string(i) + '-' + to_string(j) + '-' + curr;
if(memo.find(key) != memo.end())
return;
memo[key] = true;
if(s[i-1] == t[j-1]){
curr.push_back(s[i-1]);
findAllLcs(s,t,i-1,j-1,dp, curr, res, memo);
}else{
if(dp[i][j] == dp[i-1][j])
findAllLcs(s,t,i-1,j,dp, curr, res, memo);
if(dp[i][j] == dp[i][j-1])
findAllLcs(s,t,i,j-1,dp, curr, res, memo);
}
}
vector<string> all_longest_common_subsequences(string s, string t) {
int m = s.length(), n=t.length();
vector<vector<int>> dp(m+1, vector<int>(n+1, 0));
for(int i=1;i<=m;i++)
{
for(int j=1;j<=n;j++)
{
if(s[i-1]==t[j-1])
dp[i][j] = 1 + dp[i-1][j-1];
else
dp[i][j] = max(dp[i-1][j], dp[i][j-1]);
}
}
set<string> res;
string curr="";
map<string, bool> memo;
findAllLcs(s,t,m,n,dp, curr, res, memo);
return vector<string>(res.begin(), res.end());
}
};
Time Complexity: O(m x n) + O(m x n)
Tabulation#
Build the LCS table bottom-up, then reconstruct a single LCS by walking the table backwards: follow the diagonal on a match, otherwise move toward the larger neighbor (up if dp[i-1][j] > dp[i][j-1], else left). This produces one valid LCS in O(m + n) backtracking time. Use the "Approach" above if all LCS strings are needed.
#include <bits/stdc++.h>
using namespace std;
void printLCS(vector<vector<int>> &dp, string &str, string str1, string str2){
int i = str1.size();
int j = str2.size();
while(i>0 && j>0) {
if(str1[i-1] == str2[j-1]){
str = str1[i-1] + str;
i--;
j--;
}else if(dp[i-1][j]>dp[i][j-1]){
i--;
}else{
j--;
}
}
}
int lcsUtilDP(string str1, int m, string str2, int n, string &lcsStr){
vector<vector<int>> dp(m+1, vector<int>(n+1, 0));
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
if(str1[i-1]==str2[j-1]){
dp[i][j] = dp[i-1][j-1] + 1;
}else{
dp[i][j] = max(dp[i-1][j], dp[i][j-1]);
}
}
}
printLCS(dp, lcsStr, str1, str2);
return dp[m][n];
}
int lcs(string str1, string str2, string &lcsStr) {
int m = str1.size();
int n = str2.size();
return lcsUtilDP(str1, m, str2, n, lcsStr);
}
int main() {
string str1 = "bdefg";
string str2 = "bfg";
string lcsStr = "";
cout<<lcs(str1, str2, lcsStr)<<" - "<<lcsStr<<endl;
str1 = "mnop";
str2 = "mnq";
lcsStr = "";
cout<<lcs(str1, str2, lcsStr)<<" - "<<lcsStr<<endl;
return 0;
}
Time Complexity: O(m x n) + O(m + n)