Count of Subsequences of given string X in between strings Y and Z


A subsequence is a string that can be achieved from another string by removing some (possibly none or all) from a given string that may not be continuous. We are given a string and have to find the number of subsequences that are greater than equal to the given string Y and less than equal to another given string Z. We will use dynamic programming to solve the problem as brute force will take exponential time.

Brute Force Approach

The brute forces approach is to find all the subsequences of the given string X and then check if they are lying in the given range. If the current subsequence is lying in the given range then we will increase the count.

Example

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

// global varaibles
string x, y, z;
int len_x, len_y, len_z;
// recursive function 
int rec(string& cur, int i){
   // defining the base cases 
   if(i == len_x){        
      if(cur <=z && cur >= y){
         return 1; 
      } else {
         return 0;
      }
   }
   int ans = rec(cur, i + 1);    
   cur.push_back(x[i]);
   ans += rec(cur, i + 1);
   cur.pop_back();
   return ans; 
}
// function to get the number of subsequences less than Z and greater than Y
int getCount(string X, string Y, string Z){
   string cur = ""; // initializing empty string     
   // getting the length of the given strings
   len_x = X.size();
   len_y = Y.size();
   len_z = Z.size();
   // calling to the recursive function to get the result 
   return rec(cur, 0);
} 
int main(){
   // given strings 
   x = "abc";
   y = "a";
   z = "bc";    
   // edge case, if the given string y is greater than z
   // return zero 
   if(y > z){
      cout<<"The number of subsequences of given string X in between strings Y and Z are: "<<0<<endl;
   } else {
      // calling the function 
      cout<<"The number of subsequences of given string X in between strings Y and Z are: "<<getCount(x,y,z) <<endl;
   }
   return 0;
}

Output

The number of subsequences of given string X in between strings Y and Z are: 6

Time and Space Complexity

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

The space complexity of the above code is O(N), as we are using a string to store the current string.

Dynamic Programming Approach

We will make choice at each of the index, in first choice we will skip the current index value and move to the next value and for the second choice we will check if by adding the current character to the string is it in the range of not.

If by adding the current element in the string we will get result in the range then we will add to the answer. Also, if we already have visited this state then we will return already stored answer.

Example

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

// array to store the state results
int memo[105][105][2][2];
// global variables
string x, y, z;
int len_x, len_y, len_z;
// recursive function 
int rec(int i, int j, bool var1, bool var2){
   // defining the base cases 
   if(i == len_x){
      // if subsequence is empty then return 0
      if (j == 0){
         return 0;
      }
      if(var1 == false || j >= len_y){
         return 1;
      }  else {
         return 0;
      }
   } 
   // if the memo array contains this item then return it
   if(memo[i][j][var1][var2] != -1){
      return memo[i][j][var1][var2];
   } 
   // skip the current index 
   int res = rec(i + 1, j, var1, var2); 
   // variable to mark the position of current index 
   int canAdded = 0;
   if(var1 == false) {
      canAdded = 1;
   } else if ((j >= len_y) || (x[i] >= y[j])) {
      canAdded = 1; 
      var1 &= ((j < len_y) && x[i] == y[j]);
   } 
   if (var2 == false){
      canAdded ++;
   } else if((j < len_z) && x[i] <= z[j]){
      canAdded++;
      var2 &= (x[i] == z[j]);
   }
   if (canAdded == 2){
      // increase both i and j by 1
      res += rec(i + 1, j + 1, var1, var2);
   } 
   // store the answer in memo array
   memo[i][j][var1][var2] = res;
   return res;
}
// function to get the number of subsequences less than Z and greater than Y
int getCount(string X, string Y, string Z){
   // initialize the memo function 
   memset(memo, -1, sizeof(memo)); 
   // getting the lenght of the given strings
   len_x = X.size();
   len_y = Y.size();
   len_z = Z.size(); 
   // calling to the recursive function to get the result 
   return rec(0, 0, 1, 1);
} 
int main(){
   // given strings 
   x = "abc";
   y = "a";
   z = "bc";    
   // edge case, if the given string y is greater than z
   // return zero 
   if(y > z){
      cout<<"The number of subsequences of given string X in between strings Y and Z are: "<<0<<endl;
   } else {
      // calling the function 
      cout<<"The number of subsequences of given string X in between strings Y and Z are: "<<getCount(x,y,z) <<endl;
   }
   return 0;
}

Output

The number of subsequences of given string X in between strings Y and Z are: 6

Time and Space Complexity

The time complexity of the above code is O(N*N), where N is the size of the given string.

The space complexity of the above code is O(N*N), as we are using an array to store the results or the states.

Conclusion

In this tutorial, we have implemented a program to find the number of subsequences of string X that are lying in between the given strings Y and Z. We have seen the simple brute force method which is in-efficient and then we have implemented the efficient dynamic programming code with the time complexity of O(N*N) and same space complexity.

Updated on: 24-Aug-2023

59 Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements