传送门
子串可以转化为所有后缀的不同前缀
那么考虑建立后缀数组,按照 s a [ i ] sa[i] sa[i]的顺序逐步加入
当我们假如 s a [ i ] sa[i] sa[i]时,会产生 n − s a [ i ] + 1 n-sa[i]+1 n−sa[i]+1个新的前缀
但是其中,有 h e i g h t [ i ] height[i] height[i]个前缀和 s a [ i − 1 ] sa[i-1] sa[i−1]的那个后缀产生的贡献冲突了,需要减掉。
那么这里有一个问题,为什么不需要计算 s a [ i ] sa[i] sa[i]和 s a [ i − 2 ] sa[i-2] sa[i−2]重复的前缀呢??
因为 s a [ i ] sa[i] sa[i]和 s a [ i − 1 ] sa[i-1] sa[i−1]相同的前缀个数涵盖了 s a [ i ] sa[i] sa[i]和 s a [ i − 2 ] sa[i-2] sa[i−2]的
假如不是,那么按照字典序的排名就不是这样的
复杂度为预处理后缀数组的复杂度
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6+10;
char s[maxn];
int x[maxn],y[maxn],c[maxn],sa[maxn],rk[maxn],height[maxn],n,m;
void get_sa()
{
for(int i=1;i<=n;i++) ++c[x[i]=s[i]];
for(int i=2;i<=m;i++) c[i] += c[i-1];
for(int i=n;i>=1;i--) sa[c[x[i]]--] = i;
for(int k=1;k<=n;k<<=1)
{
int num = 0;
for(int i=n-k+1;i<=n;i++) y[++num] = i;
for(int i=1;i<=n;i++) if( sa[i]>k ) y[++num] = sa[i]-k;
for(int i=1;i<=m;i++) c[i] = 0;
for(int i=1;i<=n;i++) ++c[x[i]];
for(int i=2;i<=m;i++) c[i] += c[i-1];
for(int i=n;i>=1;i--) sa[c[x[y[i]]]--] = y[i], y[i] = 0;
swap(x,y);
x[sa[1]] = 1, num = 1;
for(int i=2;i<=n;i++)
x[sa[i]] = ( y[sa[i]]==y[sa[i-1]] && y[sa[i]+k]==y[sa[i-1]+k] )?num:++num;
if( num==n ) break;
m = num;//只有这么多种类型
}
}
void get_height()
{
int k = 0;
for(int i=1;i<=n;i++) rk[sa[i]] = i;
for(int i=1;i<=n;i++)
{
if( rk[i]==1 ) continue;//第一名height为0
if( k ) k--; //h[i]>=h[i-1]-1
int j = sa[rk[i]-1];
while( j+k<=n&&i+k<=n&&s[i+k]==s[j+k] ) k++;
height[rk[i]] = k;
}
}
int main()
{
int t; cin >> t;
while( t-- )
{
scanf("%s",s+1);
n = strlen( s+1 ); m = 122;
get_sa(); get_height();
int ans = n*(n+1)/2;
for(int i=2;i<=n;i++) ans -= height[i];
cout << ans << endl;
}
}