Longest Common Subsequence with no Repeating Character


In a string, a subsequence is a string that can be formed by deleting some character from it which means it contains some character from the string may be all or none and all will be present in the same order of the string. Among two strings we have to find the longest common subsequence that will not contain any repeating characters.

Sample Examples

Input

string str1 = "aabcadjmuorrrcc"
string str2 = "adbcwcadjomrorlc" 

Output

The length of the longest common subsequence is: 8

Explanation: In the above-given strings, we have the largest common subsequences “abcadjmrrc” and if we consider only the unique characters then we will get “abcdjmor” by removing 'a', 'c', and 'r' which were present two times.

Input

string str1 = “abcdedf”
string str2 =  “xayjklf”

Output

The length of the longest common subsequence is: 2

Explanation: Here, we have only two characters that are common and both of them are 'af'.

Recursion Approach

In this approach, we are going to finalize all the subsequences that are common in both the given strings and we will maintain the unique characters count so that none of the characters can be repeated.

We will create a recursive function that will take both strings as the parameters along with the pointers that indicate the current index of both strings. Also, we will pass an integer that will store the characters that are already present in the current common subsequence using the concept of the bit masking.

We will mark the bit to set bit for the ASCII value of the current character minus the ASCII value of the character 'a' to store them in the number.

Example

#include <bits/stdc++.h>
using namespace std;

// recursion function 
int rec(string& str1, string& str2, int i, int j, int bit){
   if(i == str1.size() || j == str2.size()){
      return 0;
   }
   if((str1[i] == str2[j])  && (bit & (1 << (str1[i]-'0'))) == 0){
      bit = bit | (i << (str1[i]-'0'));
      return 1 + rec(str1, str2, i+1, j+1, bit);
   } else{ 
      return max(rec(str1,str2,i+1,j,bit), rec(str1,str2,i,j+1,bit));
   }
}
// helper function
int findlen(string str1, string str2){
   int bit = 0; // integer to store the bit values 
   // calling to the recursion function to get the answer 
   int ans = rec(str1, str2, 0, 0, bit);
   return ans; // returning the answer 
}
// main function 
int main(){
   string str1 = "aabcadjmuorrrcc"; // given first string
   string str2 = "adbcwcadjomrorlc"; // given second string 
   // calling the funciton 
   cout<<"The length of the longest common subsequence is: "<<findlen(str1,str2)<<endl;
}

Output

The length of the longest common subsequence is: 8

Time and Space Complexity

The time complexity of the above code is O(N*2^N), where N is the size of the largest string among both of the given strings.

The space complexity of the above code is O(N), which is due to the recursive stack.

Memoization Approach

In the previous approach, there were the number of calls to the function was high, more precisely exponential calls were there making the approach inefficient. So, we will use the memoization method to store the results of the calls that we have already computed.

We will use the hash map to store the key in the form of the string because we are using an integer with 26 bits (to mark the unique characters) makes creating the dp table using an array impossible.

We will follow the previous code and will just add some new lines of the memoization technique that are defining and initializing the map. Then store the results in the map and check if the key is already present in the map or not.

Example

#include <bits/stdc++.h>
using namespace std;

// map for memoization 
map<string, int> memo;
// recursion function 
int rec(string& str1, string& str2, int i, int j, int bit){
   if(i == str1.size() || j == str2.size()){
      return 0;
   }
   string str = to_string(i) + to_string(j) + to_string(bit);
   if(memo.find(str) != memo.end()){
      return memo[str];
   }
   if((str1[i] == str2[j])  && (bit & (1 << (str1[i]-'0'))) == 0){
      bit = bit | (i << (str1[i]-'0'));
      memo[str] = 1 + rec(str1, str2, i+1, j+1, bit);
   } else{ 
      memo[str] = max(rec(str1,str2,i+1,j,bit), rec(str1,str2,i,j+1,bit));
   }
   return memo[str];
}
// helper function
int findlen(string str1, string str2){
   int bit = 0; // integer to store the bit values 
   memo = {}; // initializing the map
   // calling to the recursion function to get the answer 
   int ans = rec(str1, str2, 0, 0, bit);
   return ans; // returning the answer 
}
// main function 
int main(){
   string str1 = "aabcadjmuorrrcc"; // given first string
   string str2 = "adbcwcadjomrorlc"; // given second string 
   // calling the function 
   cout<<"The length of the longest common subsequence is: "<<findlen(str1,str2)<<endl;
}

Output

The length of the longest common subsequence is: 8

Time and Space Complexity

The time complexity of the above code is O(N*M), where N and M are the sizes of the given strings.

The space complexity of the above code is O(N*M), because we are using the hashmap to store the memoization results.

Conclusion

In this tutorial, we have implemented a program to find the longest subsequence among the two given strings that will not contain any repeated character. First, we implemented the recursive approach, and then after we used the hashmap to store the results of the calls that are already computed and the returned the answer. The time complexity of the above code is O(N*M) and the space complexity is also the same.

Updated on: 31-Aug-2023

90 Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements