Given a string s, determine the number of distinct substrings (including the empty substring) of the given string.
A string B is a substring of a string A if B can be obtained by deleting several characters (possibly none) from the start of A and several characters (possibly none) from the end of A. Two strings X and Y are considered different if there is at least one index i such that the character of X at index i is different from the character of Y at index i (X[i] != Y[i]).
Input : s = "aba"
Output : 6
Explanation : The distinct substrings are "a", "ab", "ba", "b", "aba", "".
Input : s = "abc"
Output : 7
Explanation : The distinct substrings are "a", "ab", "abc", "b", "bc", "c", "".
Input : s = "aaabc"
For detailed dry run watch the video.
#include <bits/stdc++.h>
using namespace std;
class Solution {
public:
// To Calculate Distinct Substrings
int countDistinctSubstring(string s) {
set<string> substrings;
// Iterate through each character of string
for (int i = 0; i < s.length(); ++i) {
string substring = "";
/* Construct all possible substrings
starting from the current character*/
for (int j = i; j < s.length(); ++j) {
substring += s[j];
substrings.insert(substring);
}
}
// Include empty substring
substrings.insert("");
// Return number of distinct substrings
return substrings.size();
}
};
int main() {
Solution solution;
string input = "abc";
cout << "Number of distinct substrings: " << solution.countDistinctSubstring(input) << endl;
return 0;
}
import java.util.*;
public class Solution {
// To Calculate Distinct Substrings
public int countDistinctSubstring(String s) {
Set<String> substrings = new HashSet<>();
// Iterate through each character of string
for (int i = 0; i < s.length(); ++i) {
String substring = "";
/* Construct all possible substrings
starting from the current character */
for (int j = i; j < s.length(); ++j) {
substring += s.charAt(j);
substrings.add(substring);
}
}
// Include empty substring
substrings.add("");
// Return number of distinct substrings
return substrings.size();
}
public static void main(String[] args) {
Solution solution = new Solution();
String input = "abc";
System.out.println("Number of distinct substrings: " + solution.countDistinctSubstring(input));
}
}
class Solution:
def countDistinctSubstring(self, s: str) -> int:
substrings = set()
# Iterate through each character of the string
for i in range(len(s)):
substring = ""
''' Construct all possible substrings
starting from the current character '''
for j in range(i, len(s)):
substring += s[j]
substrings.add(substring)
# Include empty substring
substrings.add("")
# Return number of distinct substrings
return len(substrings)
# Test the solution
solution = Solution()
input_str = "abc"
print("Number of distinct substrings:", solution.countDistinctSubstring(input_str))
class Solution {
// To Calculate Distinct Substrings
countDistinctSubstring(s) {
let substrings = new Set();
// Iterate through each character of the string
for (let i = 0; i < s.length; ++i) {
let substring = "";
/* Construct all possible substrings
starting from the current character */
for (let j = i; j < s.length; ++j) {
substring += s[j];
substrings.add(substring);
}
}
// Include empty substring
substrings.add("");
// Return number of distinct substrings
return substrings.size;
}
}
// Test the solution
const solution = new Solution();
const input = "abc";
console.log("Number of distinct substrings:", solution.countDistinctSubstring(input));
For detailed dry run watch the video.
#include <bits/stdc++.h>
using namespace std;
// Node structure
struct Node {
Node* links[26];
bool containsKey(char ch) {
return links[ch - 'a'] != nullptr;
}
void put(char ch, Node* node) {
links[ch - 'a'] = node;
}
Node* get(char ch) {
return links[ch - 'a'];
}
};
class Solution {
public:
// Count number of distinct substrings in string
int countDistinctSubstring(string s) {
int c = 0;
// Root node of the trie
Node* root = new Node();
// Iterate all starting positions of substrings
for (int i = 0; i < s.size(); i++) {
Node* node = root;
// Iterate through characters
for (int j = i; j < s.size(); j++) {
/*If the current character is not
a child of the current node,
insert it as a new child node*/
if (!node->containsKey(s[j])) {
c++;
// Insert new child node for character s[j]
node->put(s[j], new Node());
}
// Move to the child node
node = node->get(s[j]);
}
}
// Clean up the allocated memory
deleteTrie(root);
/*Return the total
count of distinct
substrings including
the empty string*/
return c+1;
}
private:
// Freeing up memory
void deleteTrie(Node* node) {
for (int i = 0; i < 26; i++) {
if (node->links[i] != nullptr) {
deleteTrie(node->links[i]);
}
}
delete node;
}
};
// Main function
int main() {
Solution solution;
// Test case 1
string s1 = "aabbaba";
cout << "Input: " << s1 << endl;
cout << "Number of distinct substrings: " << solution.countDistinctSubstring(s1) << endl;
// Test case 2
string s2 = "abcdefg";
cout << "Input: " << s2 << endl;
cout << "Number of distinct substrings: " << solution.countDistinctSubstring(s2) << endl;
return 0;
}
import java.util.*;
class Node {
Node[] links = new Node[26]; // For 26 lowercase English letters
boolean containsKey(char ch) {
return links[ch - 'a'] != null;
}
void put(char ch, Node node) {
links[ch - 'a'] = node;
}
Node get(char ch) {
return links[ch - 'a'];
}
}
class Solution {
// Count number of distinct substrings in string
public int countDistinctSubstring(String s) {
int count = 0;
// Root node of the Trie
Node root = new Node();
// Iterate over all starting positions of substrings
for (int i = 0; i < s.length(); i++) {
Node node = root;
// Iterate through characters of the substring starting at index i
for (int j = i; j < s.length(); j++) {
char currentChar = s.charAt(j);
// If the current character is not a child of the current node, insert it
if (!node.containsKey(currentChar)) {
count++;
// Insert new child node for character s[j]
node.put(currentChar, new Node());
}
// Move to the child node
node = node.get(currentChar);
}
}
// Return the total count of distinct substrings including the empty string
return count + 1; // +1 to count the empty substring
}
}
public class Main {
public static void main(String[] args) {
Solution solution = new Solution();
// Test case 1
String s1 = "aba";
System.out.println("Input: " + s1);
System.out.println("Number of distinct substrings: " + solution.countDistinctSubstring(s1));
// Test case 2
String s2 = "abc";
System.out.println("Input: " + s2);
System.out.println("Number of distinct substrings: " + solution.countDistinctSubstring(s2));
}
}
class Node:
def __init__(self):
self.links = [None] * 26
def containsKey(self, ch):
return self.links[ord(ch) - ord('a')] is not None
def put(self, ch, node):
self.links[ord(ch) - ord('a')] = node
def get(self, ch):
return self.links[ord(ch) - ord('a')]
class Solution:
# Count number of distinct substrings in string
def countDistinctSubstring(self, s):
c = 0
# Root node of the trie
root = Node()
# Iterate all starting positions of substrings
for i in range(len(s)):
node = root
# Iterate through characters
for j in range(i, len(s)):
'''If the current character is not
a child of the current node,
insert it as a new child node'''
if not node.containsKey(s[j]):
c += 1
# Insert new child node for character s[j]
node.put(s[j], Node())
# Move to the child node
node = node.get(s[j])
# Clean up the allocated memory
self.deleteTrie(root)
'''Return the total
count of distinct
substrings including
the empty string'''
return c + 1
# Freeing up memory
def deleteTrie(self, node):
for i in range(26):
if node.links[i] is not None:
self.deleteTrie(node.links[i])
# Main function
if __name__ == "__main__":
solution = Solution()
# Test case 1
s1 = "aabbaba"
print("Input:", s1)
print("Number of distinct substrings:", solution.countDistinctSubstring(s1))
# Test case 2
s2 = "abcdefg"
print("Input:", s2)
print("Number of distinct substrings:", solution.countDistinctSubstring(s2))
class Node {
constructor() {
this.links = new Array(26).fill(null);
}
containsKey(ch) {
return this.links[ch.charCodeAt(0) - 'a'.charCodeAt(0)] !== null;
}
put(ch, node) {
this.links[ch.charCodeAt(0) - 'a'.charCodeAt(0)] = node;
}
get(ch) {
return this.links[ch.charCodeAt(0) - 'a'.charCodeAt(0)];
}
}
class Solution {
// Count number of distinct substrings in string
countDistinctSubstring(s) {
let c = 0;
// Root node of the trie
let root = new Node();
// Iterate all starting positions of substrings
for (let i = 0; i < s.length; i++) {
let node = root;
// Iterate through characters
for (let j = i; j < s.length; j++) {
/*If the current character is not
a child of the current node,
insert it as a new child node*/
if (!node.containsKey(s[j])) {
c++;
// Insert new child node for character s[j]
node.put(s[j], new Node());
}
// Move to the child node
node = node.get(s[j]);
}
}
// Clean up the allocated memory
this.deleteTrie(root);
/*Return the total
count of distinct
substrings including
the empty string*/
return c + 1;
}
// Freeing up memory
deleteTrie(node) {
for (let i = 0; i < 26; i++) {
if (node.links[i] !== null) {
this.deleteTrie(node.links[i]);
}
}
}
}
// Main function
const solution = new Solution();
// Test case 1
const s1 = "aabbaba";
console.log("Input:", s1);
console.log("Number of distinct substrings:", solution.countDistinctSubstring(s1));
// Test case 2
const s2 = "abcdefg";
console.log("Input:", s2);
console.log("Number of distinct substrings:", solution.countDistinctSubstring(s2));
Q: Can we use hashing to count distinct substrings?
A: Yes, Rolling Hashing (Rabin-Karp) can be used to check substring uniqueness, but handling hash collisions requires additional techniques.
Q: How can we modify this approach for distinct subsequences instead of substrings?
A: A Dynamic Programming (DP) approach is needed, as subsequences allow character deletions from anywhere, unlike substrings.
Q: How would the solution change if we needed only distinct substrings of length k?
A: Instead of counting all substrings, we could maintain a rolling hash (or Suffix Array) and filter only substrings of length k.