$\newcommand{\O}{\mathrm{O}}$

文字列 $s$ 内に含まれる相異なる(連続な)部分文字列の数を求めるアルゴリズム.
結論から述べると接尾辞配列 sa および隣接する接尾辞の最長共通接頭辞の長さを表す配列 lcp を構築することで可能になる.
これらの概念についてはある程度有名なのでここには書かない.
例えば $S =$ "abcbcba" とする. このとき $sa =$ {"a", "abcbcba", "ba", "bcba", "bcbcba", "cba", "cbcba"} となる.
接尾辞配列を前から見ていき, まず先頭の "a" を見て "a" を部分文字列として数え上げる. その次に "abcbcba" を見て "ab", "abc", "abcb", "abcbc", "abcbcb", "abcbcba" を数え上げる.
このとき部分文字列の数は ("abcbcba" の長さ) - ("a" と "abcbcba" の最長共通接頭辞) のようにして計算することができる. 言い換えると "ab" から始まる部分文字列を数え上げている.
同様に "ba" を見て "b", "ba" を数え上げる. 部分文字列の数は ("ba" の長さ) - ("abcbcba" と "ba" の最長共通接頭辞) に対応する. 言い換えると "b" から始まる部分文字列を数え上げている.
同様に "bcba" を見て "bc", "bcb", "bcba" を数え上げる. 部分文字列の数は ("bcba" の長さ) - ("ba" と "bcba" の最長共通接頭辞) に対応する. 言い換えると "bc" から始まる部分文字列を数え上げている.
これを繰り返して最終的に $22$ が答えとなる(空文字を部分文字列の $1$ つとして考えている).
以下の実装では接尾辞配列の構築に $\O (|S| \log^2 |S|)$ 時間かけているが SA_IS などの線形時間での接尾辞配列構築アルゴリズムを用いることで $\O (|S|)$ 時間で計算することが可能である.
時間計算量: $\O( |S| \log^2 |S|)$ (線形時間での接尾辞配列構築アルゴリズムを用いることで $\O (|S|)$ 時間に改善可能)
class SuffixArray {
public:
int sz, index1, index2;
vector<int> rnk, tmp, sa, lcp;
SuffixArray(const string& arg)
: sz((int)arg.size()), rnk(sz+1), tmp(sz+1), sa(sz+1), lcp(sz+1){
make_sa(arg), make_lcp(arg);
}
void make_sa(const string& s){
index1 = sz;
for(int i = 0; i <= index1; ++i){
sa[i] = i;
rnk[i] = (i < index1) ? s[i] : -1;
}
auto comp = [&](const int i, const int j){
if(rnk[i] != rnk[j]){
return rnk[i] < rnk[j];
}else{
const int ri = (i + index2 <= index1) ? rnk[i + index2] : -1;
const int rj = (j + index2 <= index1) ? rnk[j + index2] : -1;
return ri < rj;
}
};
for(index2 = 1; index2 <= index1; index2 *= 2){
sort(sa.begin(), sa.end(), comp);
tmp[sa[0]] = 0;
for(int i = 1; i <= index1; ++i){
tmp[sa[i]] = tmp[sa[i-1]] + (comp(sa[i-1], sa[i]) ? 1 : 0);
}
for(int i = 0; i <= index1; ++i){
rnk[i] = tmp[i];
}
}
}
void make_lcp(const string& s){
for(int i = 0; i <= sz; ++i){
rnk[sa[i]] = i;
}
int h = 0;
lcp[0] = 0;
for(int i = 0; i < sz; ++i){
int j = sa[rnk[i]-1];
if(h > 0){
--h;
}
for(; j + h < sz && i + h < sz; ++h){
if(s[j+h] != s[i+h]){
break;
}
}
lcp[rnk[i]-1] = h;
}
}
};
long long solve(const string& s)
{
int n = (int)s.size();
long long ans = 1;
SuffixArray SA(s);
for(int i = 0; i < n; ++i){
ans += n - SA.sa[i+1] - SA.lcp[i];
}
return ans;
}
yosupo さんの library checker : Number of Substrings 提出コード