Given a string, find the count of distinct subsequences of it.
Examples:
Input: str = “gfg”
Output: 7
Explanation: The seven distinct subsequences are “”, “g”, “f”, “gf”, “fg”, “gg” and “gfg”Input: str = “ggg”
Output: 4
Explanation: The four distinct subsequences are “”, “g”, “gg” and “ggg”
The problem of counting distinct subsequences is easy if all characters of input string are distinct. The count is equal to nC0 + nC1 + nC2 + … nCn = 2n.
How to count distinct subsequences when there can be repetition in input string?
A Simple Solution to count distinct subsequences in a string with duplicates is to generate all subsequences. For every subsequence, store it in a hash table if it doesn’t exist already. The time complexity of this solution is exponential and it requires exponential extra space.
Method 1(Naive Approach): Using a set (without Dynamic Programming)
Approach: Generate all the possible subsequences of a given string. The subsequences of a string can be generated in the following manner:
- Include a particular element(say ith) in the output array and recursively call the function for the rest of the input string. This results in the subsequences of a string having ith character.
- Exclude a particular element(say ith) and recursively call the function for the rest of the input string. This contains all the subsequences which don’t have the ith character.
Once we have generated a subsequence, in the base case of the function we insert that generated subsequence in an unordered set. An unordered Set is a Data structure, that stores distinct elements in an unordered manner. This way we insert all the generated subsequences in the set and print the size of the set as our answer because at last, the set will contain only distinct subsequences.
Implementation:
C++
// C++ program to print distinct // subsequences of a given string #include <bits/stdc++.h> using namespace std; // Create an empty set to store the subsequences unordered_set<string> sn; // Function for generating the subsequences void subsequences( char s[], char op[], int i, int j) { // Base Case if (s[i] == '\0' ) { op[j] = '\0' ; // Insert each generated // subsequence into the set sn.insert(op); return ; } // Recursive Case else { // When a particular character is taken op[j] = s[i]; subsequences(s, op, i + 1, j + 1); // When a particular character isn't taken subsequences(s, op, i + 1, j); return ; } } // Driver Code int main() { char str[] = "ggg" ; int m = sizeof (str) / sizeof ( char ); int n = pow (2, m) + 1; // Output array for storing // the generating subsequences // in each call char op[m+1]; //extra one for having \0 at the end // Function Call subsequences(str, op, 0, 0); // Output will be the number // of elements in the set cout << sn.size(); sn.clear(); return 0; // This code is contributed by Kishan Mishra } |
Java
// java program to print distinct // subsequences of a given string import java.io.*; import java.lang.Math; import java.util.*; class GFG { // Function for generating the subsequences public static void subsequences(Set<String> sn, char [] s, char [] op, int i, int j, int n) { // Base Case if (i == n) { op[j] = '\0' ; // Insert each generated // subsequence into the set sn.add(String.valueOf(op)); return ; } // Recursive Case else { // When a particular character is taken op[j] = s[i]; subsequences(sn, s, op, i + 1 , j + 1 , n); // When a particular character isn't taken subsequences(sn, s, op, i + 1 , j, n); return ; } } public static void main(String[] args) { char [] str = { 'g' , 'g' , 'g' }; int m = str.length; int n = ( int )Math.pow( 2 , m) + 1 ; // Create an empty set to store the subsequences Set<String> sn = new HashSet<String>(); // Output array for storing // the generating subsequences // in each call char [] op = new char [m + 1 ]; // extra one for having // \0 at the end // Function Call subsequences(sn, str, op, 0 , 0 , m); // Output will be the number // of elements in the set System.out.println(sn.size()); sn.clear(); // This code is contributed by Aditya Sharma } } |
Python3
# Python3 program to print # distinct subsequences of # a given string import math # Create an empty set # to store the subsequences sn = [] global m m = 0 # Function for generating # the subsequences def subsequences(s, op, i, j): # Base Case if (i = = m): op[j] = None temp = "".join([i for i in op if i]) # Insert each generated # subsequence into the set sn.append(temp) return # Recursive Case else : # When a particular # character is taken op[j] = s[i] subsequences(s, op, i + 1 , j + 1 ) # When a particular # character isn't taken subsequences(s, op, i + 1 , j) return # Driver Code str = "ggg" m = len ( str ) n = int (math. pow ( 2 , m) + 1 ) # Output array for storing # the generating subsequences # in each call op = [ None for i in range (n)] # Function Call subsequences( str , op, 0 , 0 ) # Output will be the number # of elements in the set print ( len ( set (sn))) # This code is contributed by avanitrachhadiya2155 |
C#
// C# program to print distinct // subsequences of a given string using System; using System.Collections.Generic; class GFG { // Function for generating the subsequences public static void subsequences(HashSet< string > sn, char [] s, char [] op, int i, int j, int n) { // Base Case if (i == n) { op[j] = '\0' ; // Insert each generated // subsequence into the set sn.Add( string .Join( "" , op)); return ; } // Recursive Case else { // When a particular character is taken op[j] = s[i]; subsequences(sn, s, op, i + 1, j + 1, n); // When a particular character isn't taken subsequences(sn, s, op, i + 1, j, n); return ; } } public static void Main( string [] args) { char [] str = { 'g' , 'g' , 'g' }; int m = str.Length; int n = ( int )Math.Pow(2, m) + 1; // Create an empty set to store the subsequences HashSet< string > sn = new HashSet< string >(); // Output array for storing // the generating subsequences // in each call char [] op = new char [m + 1]; // extra one for having // \0 at the end // Function Call subsequences(sn, str, op, 0, 0, m); // Output will be the number // of elements in the set Console.WriteLine(sn.Count); } } // This code is contributed by Abhijeet Kumar(abhijeet19403) |
Javascript
<script> // Javascript program to print distinct // subsequences of a given string // Create an empty set to store the subsequences let sn = new Set(); let m = 0; // Function for generating the subsequences function subsequences(s, op, i, j) { // Base Case if (i == m) { op[j] = '\0' ; // Insert each generated // subsequence into the set sn.add(op.join( "" )); return ; } // Recursive Case else { // When a particular character is taken op[j] = s[i]; subsequences(s, op, i + 1, j + 1); // When a particular character isn't taken subsequences(s, op, i + 1, j); return ; } } // Driver Code let str= "ggg" ; m = str.length; let n = Math.pow(2, m) + 1; // Output array for storing // the generating subsequences // in each call let op= new Array(n); // Function Call subsequences(str, op, 0, 0); // Output will be the number // of elements in the set document.write(sn.size); // This code is contributed by patel2127 </script> |
4
Time Complexity: O(2^n)
Auxiliary Space: O(2^n)
where n is the length of the string.
Method 2(Efficient Approach): Using Dynamic Programming
An Efficient Solution doesn’t require the generation of subsequences.
Let countSub(n) be count of subsequences of
first n characters in input string. We can
recursively write it as below.
countSub(n) = 2*Count(n-1) - Repetition
If current character, i.e., str[n-1] of str has
not appeared before, then
Repetition = 0
Else:
Repetition = Count(m)
Here m is index of previous occurrence of
current character. We basically remove all
counts ending with previous occurrence of
current character.
How does this work?
If there are no repetitions, then count becomes double of count for n-1 because we get count(n-1) more subsequences by adding current character at the end of all subsequences possible with n-1 length.
If there are repetitions, then we find a count of all distinct subsequences ending with the previous occurrence. This count can be obtained by recursively calling for an index of the previous occurrence.
Since the above recurrence has overlapping subproblems, we can solve it using Dynamic Programming.
Below is the implementation of the above idea.
C++
// C++ program to count number of distinct // subsequences of a given string. #include <bits/stdc++.h> using namespace std; const int MAX_CHAR = 256; // Returns count of distinct subsequences of str. int countSub(string str) { // Create an array to store index // of last vector< int > last(MAX_CHAR, -1); // Length of input string int n = str.length(); // dp[i] is going to store count of distinct // subsequences of length i. int dp[n + 1]; // Empty substring has only one subsequence dp[0] = 1; // Traverse through all lengths from 1 to n. for ( int i = 1; i <= n; i++) { // Number of subsequences with substring // str[0..i-1] dp[i] = 2 * dp[i - 1]; // If current character has appeared // before, then remove all subsequences // ending with previous occurrence. if (last[str[i - 1]] != -1) dp[i] = dp[i] - dp[last[str[i - 1]]]; // Mark occurrence of current character last[str[i - 1]] = (i - 1); } return dp[n]; } // Driver code int main() { cout << countSub( "gfg" ); return 0; } |
Java
// Java program to count number of distinct // subsequences of a given string. import java.util.ArrayList; import java.util.Arrays; public class Count_Subsequences { static final int MAX_CHAR = 256 ; // Returns count of distinct subsequences of str. static int countSub(String str) { // Create an array to store index // of last int [] last = new int [MAX_CHAR]; Arrays.fill(last, - 1 ); // Length of input string int n = str.length(); // dp[i] is going to store count of distinct // subsequences of length i. int [] dp = new int [n + 1 ]; // Empty substring has only one subsequence dp[ 0 ] = 1 ; // Traverse through all lengths from 1 to n. for ( int i = 1 ; i <= n; i++) { // Number of subsequences with substring // str[0..i-1] dp[i] = 2 * dp[i - 1 ]; // If current character has appeared // before, then remove all subsequences // ending with previous occurrence. if (last[( int )str.charAt(i - 1 )] != - 1 ) dp[i] = dp[i] - dp[last[( int )str.charAt(i - 1 )]]; // Mark occurrence of current character last[( int )str.charAt(i - 1 )] = (i - 1 ); } return dp[n]; } // Driver code public static void main(String args[]) { System.out.println(countSub( "gfg" )); } } // This code is contributed by Sumit Ghosh |
Python3
# Python3 program to count number of # distinct subsequences of a given string MAX_CHAR = 256 def countSub(ss): # create an array to store index of last last = [ - 1 for i in range (MAX_CHAR + 1 )] # length of input string n = len (ss) # dp[i] is going to store count of # discount subsequence of length of i dp = [ - 2 for i in range (n + 1 )] # empty substring has only # one subsequence dp[ 0 ] = 1 # Traverse through all lengths # from 1 to n for i in range ( 1 , n + 1 ): # number of subsequence with # substring str[0...i-1] dp[i] = 2 * dp[i - 1 ] # if current character has appeared # before, then remove all subsequences # ending with previous occurrence. if last[ ord (ss[i - 1 ])] ! = - 1 : dp[i] = dp[i] - dp[last[ ord (ss[i - 1 ])]] last[ ord (ss[i - 1 ])] = i - 1 return dp[n] # Driver code print (countSub( "gfg" )) # This code is contributed # by mohit kumar 29 |
C#
// C# program to count number of distinct // subsequences of a given string. using System; public class Count_Subsequences { static readonly int MAX_CHAR = 256; // Returns count of distinct subsequences of str. static int countSub(String str) { // Create an array to store index // of last int [] last = new int [MAX_CHAR]; for ( int i = 0; i < MAX_CHAR; i++) last[i] = -1; // Length of input string int n = str.Length; // dp[i] is going to store count of // distinct subsequences of length i. int [] dp = new int [n + 1]; // Empty substring has only one subsequence dp[0] = 1; // Traverse through all lengths from 1 to n. for ( int i = 1; i <= n; i++) { // Number of subsequences with substring // str[0..i-1] dp[i] = 2 * dp[i - 1]; // If current character has appeared // before, then remove all subsequences // ending with previous occurrence. if (last[( int )str[i - 1]] != -1) dp[i] = dp[i] - dp[last[( int )str[i - 1]]]; // Mark occurrence of current character last[( int )str[i - 1]] = (i - 1); } return dp[n]; } // Driver code public static void Main(String[] args) { Console.WriteLine(countSub( "gfg" )); } } // This code is contributed 29AjayKumar |
Javascript
<script> // Javascript program to count number of // distinct subsequences of a given string. let MAX_CHAR = 256; // Returns count of distinct subsequences // of str. function countSub(str) { // Create an array to store index // of last let last = new Array(MAX_CHAR); last.fill(-1); // Length of input string let n = str.length; // dp[i] is going to store count of distinct // subsequences of length i. let dp = new Array(n + 1); // Empty substring has only one subsequence dp[0] = 1; // Traverse through all lengths from 1 to n. for (let i = 1; i <= n; i++) { // Number of subsequences with substring // str[0..i-1] dp[i] = 2 * dp[i - 1]; // If current character has appeared // before, then remove all subsequences // ending with previous occurrence. if (last[str[i - 1].charCodeAt()] != -1) dp[i] = dp[i] - dp[last[str[i - 1].charCodeAt()]]; // Mark occurrence of current character last[str[i - 1].charCodeAt()] = (i - 1); } return dp[n]; } // Driver code document.write(countSub( "gfg" )); // This code is contributed by mukesh07 </script> |
7
Time Complexity: O(n)
Auxiliary Space: O(n)
Method 3: Using Map
Idea:
Let’s say we have 2 variables : `allCount` which adds up total distinct subsequence count and `levelCount` which stores the count of subsequences ending at index i. To find repetitions we will store the most recent levelCount for each character. Finally we will see how we can determine `allCount` using the `levelCount` variable.
Below is the steps to solve the problem:
- Declare a map and store all the characters by initializing -1.
- Start a loop to iterate through the characters of the input string s.
- Inside the loop, when i (the current index) is 0, this is the first character in the string.
- Set allCount to 1 since the first character is always unique.
- Update the map mp with the index 1 for the first character c.
- For characters at positions other than 0:
- Calculate the current levelCount as allCount + 1, representing the number of unique substrings at the current level.
-
- If mp is less than 0, it means the character is new (has not been seen before in this substring). In this case:
- Increment allCount by levelCount to account for the new character.
- If mp is not less than 0, it means the character has been seen before in this substring. In this case:
- Adjust allCount by adding levelCount – mp to account for the fact that some substrings may have been counted already.
- Update the map mp with the current levelCount for the character c since this is the latest level of uniqueness.
C++
// C++ program for above approach #include <bits/stdc++.h> using namespace std; // Returns count of distinct // subsequences of str. int countSub(string s) { map< char , int > mp; // Iterate from 0 to s.length() for ( int i = 0; i < s.length(); i++) { mp[s[i]] = -1; } int allCount = 0; int levelCount = 0; // Iterate from 0 to s.length() for ( int i = 0; i < s.length(); i++) { char c = s[i]; // Check if i equal to 0 if (i == 0) { allCount = 1; mp = 1; levelCount = 1; continue ; } // Replace levelCount with // allCount + 1 levelCount = allCount + 1; // If map is less than 0 if (mp < 0) { allCount = allCount + levelCount; } else { allCount = allCount + levelCount - mp; } mp = levelCount; } // Return answer return allCount; } // Driver code int main() { string list[] = { "abab" , "gfg" }; for (string s : list) { int cnt = countSub(s); int withEmptyString = cnt + 1; cout << "With empty string count for " << s << " is " << withEmptyString << endl; cout << "Without empty string count for " << s << " is " << cnt << endl; } return 0; } // This code is contributed by divyeshrabadiya07 |
Java
// Java Program for above approach import java.io.*; import java.util.*; class SubsequenceCount { // Returns count of distinct // subsequences of str. public static int countSub(String s) { HashMap<Character, Integer> map = new HashMap<Character, Integer>(); // Iterate from 0 to s.length() for ( int i = 0 ; i < s.length(); i++) { map.put(s.charAt(i), - 1 ); } int allCount = 0 ; int levelCount = 0 ; // Iterate from 0 to s.length() for ( int i= 0 ;i<s.length();i++) { char c = s.charAt(i); // Check if i equal to 0 if (i== 0 ) { allCount = 1 ; map.put(c, 1 ); levelCount = 1 ; continue ; } // Replace levelCount with // allCount + 1 levelCount = allCount + 1 ; // If map is less than 0 if (map.get(c)< 0 ) { allCount = allCount + levelCount; } else { allCount = allCount + levelCount - map.get(c); } map.put(c,levelCount); } // Return answer return allCount; } // Driver Code public static void main(String[] args) { List<String> list = Arrays.asList( "abab" , "gfg" ); for (String s : list) { int cnt = countSub(s); int withEmptyString = cnt+ 1 ; System.out.println( "With empty string count for " + s + " is " + withEmptyString); System.out.println( "Without empty string count for " + s + " is " + cnt); } } } //Code is contributed by abhisht7 |
Python3
# Python3 program for above approach # Returns count of distinct # subsequences of str. def countSub(s): Map = {} # Iterate from 0 to length of s for i in range ( len (s)): Map [s[i]] = - 1 allCount = 0 levelCount = 0 # Iterate from 0 to length of s for i in range ( len (s)): c = s[i] # Check if i equal to 0 if (i = = 0 ): allCount = 1 Map = 1 levelCount = 1 continue # Replace levelCount with # allCount + 1 levelCount = allCount + 1 # If map is less than 0 if ( Map < 0 ): allCount = allCount + levelCount else : allCount = allCount + levelCount - Map
Map = levelCount # Return answer return allCount # Driver Code List = [ "abab" , "gfg" ] for s in List : cnt = countSub(s) withEmptyString = cnt + 1 print ( "With empty string count for" , s, "is" , withEmptyString) print ( "Without empty string count for" , s, "is" , cnt) # This code is contributed by rag2127 |
C#
// C# Program for above approach using System; using System.Collections.Generic; class GFG{ // Returns count of distinct // subsequences of str. public static int countSub(String s) { Dictionary< char , int > map = new Dictionary< char , int >(); // Iterate from 0 to s.length() for ( int i = 0; i < s.Length; i++) { if (!map.ContainsKey(s[i])) { map.Add(s[i], -1); } } int allCount = 0; int levelCount = 0; // Iterate from 0 to s.length() for ( int i = 0; i < s.Length; i++) { char c = s[i]; // Check if i equal to 0 if (i == 0) { allCount = 1; if (!map.ContainsKey(c)) { map.Add(c, 1); } else { map = 1; } levelCount = 1; continue ; } // Replace levelCount with // allCount + 1 levelCount = allCount + 1; // If map is less than 0 if (map.ContainsKey(c)) { if (map < 0) { allCount = (allCount + levelCount); } else { allCount = (allCount + levelCount - map); } } if (!map.ContainsKey(c)) { map.Add(c, levelCount); } else { map = levelCount; } } // Return answer return allCount; } // Driver Code static void Main() { List< string > list = new List< string >(); list.Add( "abab" ); list.Add( "gfg" ); foreach ( string s in list) { int cnt = countSub(s); int withEmptyString = cnt + 1; Console.WriteLine( "With empty string count for " + s + " is " + withEmptyString); Console.WriteLine( "Without empty string count for " + s + " is " + cnt); } } } // This code is contributed by divyesh072019 |
Javascript
// Javascript Program for above approach // Returns count of distinct // subsequences of str. function countSub(s) { let map = new Map(); // Iterate from 0 to s.length() for (let i = 0; i < s.length; i++) { map.set(s[i], -1); } let allCount = 0; let levelCount = 0; // Iterate from 0 to s.length() for (let i=0;i<s.length;i++) { let c = s[i]; // Check if i equal to 0 if (i==0) { allCount = 1; map.set(c,1); levelCount = 1; continue ; } // Replace levelCount with // allCount + 1 levelCount = allCount + 1; // If map is less than 0 if (map.get(c)<0) { allCount = allCount + levelCount; } else { allCount = allCount + levelCount - map.get(c); } map.set(c,levelCount); } // Return answer return allCount; } // Driver Code let list=[ "abab" , "gfg" ]; for (let i=0;i<list.length;i++) { let cnt = countSub(list[i]); let withEmptyString = cnt+1; console.log( "With empty string count for " + list[i] + " is " + withEmptyString); console.log( "Without empty string count for " + list[i] + " is " + cnt); } // This code is contributed by unknown2108 |
With empty string count for abab is 12 Without empty string count for abab is 11 With empty string count for gfg is 7 Without empty string count for gfg is 6
Time Complexity: O(n)
Space Complexity: O(1)
Ready to dive in? Explore our Free Demo Content and join our DSA course, trusted by over 100,000 neveropen!