#include <iostream>
#include <string>
#include <vector>
#include <numeric>
#include <algorithm>
using namespace std;
// A struct to help with sorting suffixes in the doubling algorithm
struct Suffix {
int index; // original index of suffix
int rank[2]; // ranks of the two halves
};
// Custom comparison function for sorting Suffix structs
int compareSuffixes(const Suffix& a, const Suffix& b) {
if (a.rank[0] != b.rank[0]) {
return a.rank[0] < b.rank[0];
}
return a.rank[1] < b.rank[1];
}
// Function to build the Suffix Array using the doubling algorithm (O(n log n))
vector<int> buildSuffixArray_doubling(const string& s) {
int n = s.length();
if (n == 0) return {};
vector<Suffix> suffixes(n);
vector<int> suffix_array(n);
vector<int> new_rank(n);
// Initial sort (prefixes of length 1)
for (int i = 0; i < n; ++i) {
suffixes[i].index = i;
suffixes[i].rank[0] = s[i] - 'a'; // Assuming lowercase English alphabet
suffixes[i].rank[1] = (i + 1 < n) ? (s[i + 1] - 'a') : -1;
}
sort(suffixes.begin(), suffixes.end(), compareSuffixes);
// Iterative doubling
for (int k = 4; k < 2 * n; k *= 2) {
int rank_prev = 0;
suffixes[0].rank[0] = 0;
new_rank[suffixes[0].index] = 0;
for (int i = 1; i < n; ++i) {
// Assign ranks for current length
if (suffixes[i].rank[0] == suffixes[i - 1].rank[0] &&
suffixes[i].rank[1] == suffixes[i - 1].rank[1]) {
rank_prev = suffixes[i].rank[0];
suffixes[i].rank[0] = rank_prev;
} else {
suffixes[i].rank[0] = suffixes[i - 1].rank[0] + 1;
rank_prev = suffixes[i].rank[0];
}
new_rank[suffixes[i].index] = suffixes[i].rank[0];
}
// Update second rank for the next iteration (length 2*k)
for (int i = 0; i < n; ++i) {
int next_index = suffixes[i].index + k / 2;
suffixes[i].rank[1] = (next_index < n) ? new_rank[next_index] : -1;
}
sort(suffixes.begin(), suffixes.end(), compareSuffixes);
}
for (int i = 0; i < n; ++i) {
suffix_array[i] = suffixes[i].index;
}
return suffix_array;
}
// Function to build the LCP array (already O(n))
vector<int> buildLCPArray(const string& s, const vector<int>& suffix_array) {
int n = s.length();
if (n == 0) return {};
vector<int> lcp(n - 1);
vector<int> rank(n);
for (int i = 0; i < n; ++i) {
rank[suffix_array[i]] = i;
}
int h = 0;
for (int i = 0; i < n; ++i) {
if (rank[i] > 0) {
int j = suffix_array[rank[i] - 1];
if (h > 0) h--;
while (i + h < n && j + h < n && s[i + h] == s[j + h]) {
h++;
}
lcp[rank[i] - 1] = h;
}
}
return lcp;
}
// Main function to count distinct substrings
long long countDistinctSubstrings(const string& s) {
int n = s.length();
if (n == 0) return 0;
// 1. Build the Suffix Array using the O(n log n) doubling algorithm
vector<int> suffix_array = buildSuffixArray_doubling(s);
// 2. Build the LCP Array using Kasai's algorithm (O(n))
vector<int> lcp_array = buildLCPArray(s, suffix_array);
// 3. Count distinct substrings using the formula
long long total_possible_substrings = static_cast<long long>(n) * (n + 1) / 2;
long long lcp_sum = accumulate(lcp_array.begin(), lcp_array.end(), 0LL);
return total_possible_substrings - lcp_sum;
}
int main() {
string s1 = "ababa";
cout << "The number of distinct substrings in '" << s1 << "' is: "
<< countDistinctSubstrings(s1) << endl;
string s2 = "aaaaa";
cout << "The number of distinct substrings in '" << s2 << "' is: "
<< countDistinctSubstrings(s2) << endl;
string s3 = "abcdefg";
cout << "The number of distinct substrings in '" << s3 << "' is: "
<< countDistinctSubstrings(s3) << endl;
return 0;
}