Number of Distinct Substrings
Count the distinct substrings of a string (the empty substring excluded).
Open on GeeksforGeeks ↗Intuition
Generating all n²/2 substrings and dropping them into a set works but costs O(n³) time once the string copying is counted. The reframing that fixes it: every substring is a prefix of some suffix. So insert all n suffixes into a trie, and each node below the root corresponds to exactly one distinct substring — the path from the root spells it out. Duplicates collapse onto shared paths automatically, so the answer is simply the node count, with no explicit de-duplication anywhere.
Every substring is a prefix of some suffix — so inserting all suffixes into a trie and counting the new nodes created counts distinct substrings exactly. Each node corresponds to one unique substring, and the trie collapses shared prefixes automatically. Reframing "substrings" as "prefixes of suffixes" is the whole unlock.
Approach
Every substring is a prefix of a suffix
Take any substring s[i..j]; it is a prefix of the suffix starting at i. So the set of all substrings equals the set of all prefixes of all suffixes.
Insert every suffix into a trie
For each start index, walk the suffix, creating nodes only where the path does not already exist.
Count nodes, not insertions
Each non-root node is one distinct substring. Repeated substrings reuse existing nodes and therefore add nothing.
Solution & live demo
Common pitfalls
Generating substrings into a set
return len({s[i:j] for i in range(n) for j in range(i+1, n+1)})for i in range(len(s)):
node = root
for ch in s[i:]:
if ch not in node:
node[ch] = {}; count += 1Correct but stores O(n²) substrings of average length O(n) — O(n³) memory. The trie shares prefixes, so equal substrings occupy the same nodes and are never duplicated.
Counting nodes visited rather than nodes created
node = node[ch] count += 1
if ch not in node:
node[ch] = {}
count += 1Walking an existing path means that substring was already counted from an earlier suffix. Only a newly created node represents a substring never seen before.
Inserting only whole suffixes without walking each character
for i in range(len(s)):
root[s[i:]] = Truefor ch in s[i:]:
...That stores n suffixes as opaque keys and counts n, not the number of distinct substrings. The character-by-character descent is what makes every prefix of every suffix its own node.
Edge cases
Only n substrings exist ("a", "aa", "aaa") — the trie is one straight path of n nodes.
Nothing merges, so the count reaches the maximum n(n+1)/2.
Excluded by construction, since the root is not counted.