• Skip to main content
  • Skip to secondary menu
  • Skip to primary sidebar
  • Skip to footer
  • Home
  • Contact Us

All about code

  • Java
    • Spring
      • Spring Boot
      • Spring Cloud
    • Java Miscellaneous Tips
    • Java Design Patterns
  • Data Structures and Algorithms
    • Algorithms
    • Data Structures
  • Thoughts and Stories
  • AI

How to Count Number of Distinct Substrings in a String

laptop infographic online business 6087062

Introduction

Counting the number of distinct substrings in a string is a classic problem in computer science and string processing. A substring is a sequence of characters that appear consecutively within a string. For instance, the string “abc” contains substrings such as “a”, “ab”, “abc”, “b”, “bc”, and “c”. The objective is to determine the total number of distinct substrings that can be generated from a given input string.

Problem Explanation

Given a string s, determine how many distinct substrings it contains, including the empty substring (often excluded depending on the definition). The challenge lies in efficiently computing this number without redundantly generating all possible substrings.

Naive Approach and Its Limitations

A brute-force method would involve generating all substrings and storing them in a hash set to filter duplicates. While this works for small strings, it is highly inefficient with a time complexity of O(n³) in the worst case, due to substring generation and set operations.

Efficient Approach Using Suffix Trie or Suffix Automaton

A more optimal approach involves using advanced data structures like a suffix trie, suffix tree, or suffix automaton. However, these can be complex to implement. An alternative, efficient, and relatively simpler method uses the Suffix Array and Longest Common Prefix (LCP) array to count distinct substrings in O(n log n) time.

Java Implementation Using Suffix Array and LCP

Here’s an efficient Java implementation that counts the number of distinct substrings using the suffix array and LCP array:

public class DistinctSubstrings {

    public static int countDistinctSubstrings(String inpustString) {
        inpustString += "$"; // Append terminal character
        int k = inpustString.length();
        int[] suffixArr = buildSuffixArr(inpustString);
        int[] lcp = buildLCPArray(inpustString, suffixArr);

        int total = 0;
        for (int i = 1; i < k; i++) {
            int suffixLength = k - 1 - suffixArr[i]; // Exclude terminal character
            total += suffixLength - lcp[i - 1];
        }
        return total;
    }

    private static int[] buildSuffixArr(String inputString) {
        int n = inputString.length();
        Integer[] order = new Integer[n];
        for (int i = 0; i < n; i++) order[i] = i;

        int[] rankArr = new int[n];
        for (int i = 0; i < n; i++) rankArr[i] = inputString.charAt(i);

        int[] temp = new int[n];
        for (int len = 1; len < n; len *= 2) {
            final int l = len;
            Arrays.sort(order, (a, b) -> {
                if (rankArr[a] != rankArr[b]) return Integer.compare(rankArr[a], rankArr[b]);
                int ra = (a + l < n) ? rankArr[a + l] : -1;
                int rb = (b + l < n) ? rankArr[b + l] : -1;
                return Integer.compare(ra, rb);
            });

            temp[order[0]] = 0;
            for (int i = 1; i < n; i++) {
                temp[order[i]] = temp[order[i - 1]];
                if (rankArr[order[i]] != rankArr[order[i - 1]] ||
                    ((order[i] + l < n ? rankArr[order[i] + l] : -1) != 
                     (order[i - 1] + l < n ? rankArr[order[i - 1] + l] : -1))) {
                    temp[order[i]]++;
                }
            }
            System.arraycopy(temp, 0, rankArr, 0, n);
        }

        int[] sa = new int[n];
        for (int i = 0; i < n; i++) sa[i] = order[i];
        return sa;
    }

    private static int[] buildLCPArray(String inputString, int[] sa) {
        int n = inputString.length();
        int[] rankArr = new int[n];
        for (int i = 0; i < n; i++) rankArr[sa[i]] = i;

        int[] lcp = new int[n - 1];
        int h = 0;
        for (int i = 0; i < n; i++) {
            if (rankArr[i] > 0) {
                int j = sa[rankArr[i] - 1];
                while (i + h < n && j + h < n && inputString.charAt(i + h) == inputString.charAt(j + h)) {
                    h++;
                }
                lcp[rankArr[i] - 1] = h;
                if (h > 0) h--;
            }
        }
        return lcp;
    }

    public static void main(String[] args) {
        String input = "banana";
        int result = countDistinctSubstrings(input);
        System.out.println("Number of distinct substrings: " + result);
    }
}

Example Walkthrough

For the input “banana”, there are 15 unique substrings. These include: “a”, “an”, “ana”, “anan”, “anana”, “b”, “ba”, “ban”, “bana”, “banan”, “banana”, “n”, “na”, “nan”, and “nana”.
It’s important to note that the empty string is excluded from this count.

Conclusion

Counting distinct substrings can be computationally expensive with a naive approach, but leveraging the suffix array and LCP array offers an efficient and scalable solution. The Java code provided illustrates a practical implementation suitable for intermediate to advanced programmers familiar with string algorithms.

You can find the example code on Github.

Primary Sidebar

Social

  • Facebook
  • LinkedIn
  • Twitter

Archives

  • May 2026
  • April 2026
  • March 2026
  • August 2025
  • July 2025
  • June 2025
  • May 2025
  • April 2025
  • March 2025
  • December 2024
  • October 2024
  • August 2024
  • July 2024
  • March 2024
  • February 2024
  • January 2024
  • December 2023
  • September 2023
  • May 2023
  • March 2023
  • January 2023
  • November 2022
  • September 2022
  • August 2022

Recent Posts

  • Better Avoid Spring Boot Lazy-initialization @PostConstruct Trap
  • How To Handle Large Datasets in Spring Boot
  • How to Log HTTP Incoming Requests in Spring Boot
  • How to Reliably Implement Post-Commit Actions in Spring
  • Brief and Easy Explanation of HATEOAS

TAGS

Algorithms Apache POI Backtracking Date Structures Dynamic Programming engineering Graphs Greedy Horror Stories HSSF Java Java New Features Java Principles Java tips real life Sorting Spring Boot Spring Cloud Strings Trees

Footer

Privacy Policy Cookie Policy Terms and Conditions