这几天想学后缀自动机的,结果发现后缀数组的坑还空着,索性填填吧。。
POJ2774
题目链接:http://poj.org/problem?id=2774
题解:
两个字符串之间加一个分隔符,求一发
s
a
[
i
]
sa[i]
sa[i]分别位于两个字符串中的
l
c
p
lcp
lcp即可,注意细节:数组两倍大、字符集大小等
代码:
// by Balloons
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define mpr make_pair
#include <string>
#define debug() puts("okkkkkkkk")
#define rep(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
using namespace std;
typedef long long LL;
const int inf = 1 << 30;
const int maxn=200005;
char a[maxn],b[maxn],ori[maxn];
int ss[maxn],n;
int sa[maxn],bin[maxn],rk[maxn],tmp[maxn];
void getsa(char *s){
n=strlen(s);int sz=max(n,27);
for(int i=0;i<n;i++)rk[i]=s[i]-'a';
for(int i=0;i<sz;i++)bin[i]=0;
for(int i=0;i<n;i++)++bin[rk[i]];
for(int i=1;i<sz;i++)bin[i]+=bin[i-1];
for(int i=n-1;i>=0;i--)sa[--bin[rk[i]]]=i;
for(int j=1;j<=n;j<<=1){
int p=0;
for(int i=n-j;i<n;i++)tmp[p++]=i;
for(int i=0;i<n;i++)
if(sa[i]-j>=0)tmp[p++]=sa[i]-j;
for(int i=0;i<sz;i++)bin[i]=0;
for(int i=0;i<n;i++)++bin[rk[i]];
for(int i=0;i<sz;i++)bin[i]+=bin[i-1];
for(int i=n-1;i>=0;i--)sa[--bin[rk[tmp[i]]]]=tmp[i];
p=0;
tmp[sa[0]]=0;
for(int i=1;i<n;i++){
int v0=sa[i-1],v1=sa[i];int v00,v01;
if(v0+j<n)v00=rk[v0+j];else v00=-1;
if(v1+j<n)v01=rk[v1+j];else v01=-1;
if(v00==v01&&rk[v0]==rk[v1])tmp[sa[i]]=p;
else tmp[sa[i]]=++p;
}
for(int i=0;i<n;i++)rk[i]=tmp[i];
}
}
int h[maxn];
int calc(int x,int y,int res,char *s){
for(;x+res<n&&y+res<n&&s[x+res]==s[y+res];)++res;
return res;
}
void geth(char *s){
if(rk[0]==0)h[0]=0;else h[0]=calc(0,sa[rk[0]-1],0,s);
for(int i=1;i<n;i++){
if(rk[i]==0)h[i]=0;else h[i]=calc(i,sa[rk[i]-1],max(h[i-1]-1,0),s);
}
}
int main(){
// freopen("POJ2774.in","r",stdin);
// freopen("POJ2774.out","w",stdout);
scanf("%s",a);scanf("%s",b);
sprintf(ori,"%s%c%s",a,'z'+1,b);
n=strlen(ori);
int la=strlen(a),lb=strlen(b);
getsa(ori);
geth(ori);
int ans=0;
for(int i=1;i<n;i++){
if(h[sa[i]]>ans){
if(0<=sa[i]&&sa[i]<la&&la<sa[i-1])ans=h[sa[i]];
if(0<=sa[i-1]&&sa[i-1]<la&&la<sa[i])ans=h[sa[i]];
}
}
printf("%d\n",ans);
return 0;
}
SPOJ705 SPOJ694
题目链接:https://www.luogu.org/problemnew/show/SP705
题解:子串=后缀的前缀,发现恰好可以用后缀数组解决,考虑按照 s a sa sa的顺序加入,每加一个会产生 n − s a n-sa n−sa个后缀(下标从0开始),但是会有 h [ s a [ i ] ] h[sa[i]] h[sa[i]]个重复的需要减掉
代码:
// by Balloons
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define mpr make_pair
#define debug() puts("okkkkkkkk")
#define rep(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
using namespace std;
typedef long long LL;
const int inf = 1 << 30;
const int maxn=1e5+5;
int bin[maxn],sa[maxn],rk[maxn],tmp[maxn];
char s[maxn];
int n,sz;
void getsa(){
sz=max(n,128);
for(int i=0;i<n;i++)rk[i]=s[i];
for(int i=0;i<sz;i++)bin[i]=0;
for(int i=0;i<n;i++)++bin[rk[i]];
for(int i=1;i<sz;i++)bin[i]+=bin[i-1];
for(int i=n-1;i>=0;i--)sa[--bin[rk[i]]]=i;
for(int j=1;j<=n;j<<=1){
int p=0;
for(int i=n-j;i<n;i++)tmp[p++]=i;
for(int i=0;i<n;i++)if(sa[i]-j>=0)tmp[p++]=sa[i]-j;
for(int i=0;i<sz;i++)bin[i]=0;
for(int i=0;i<n;i++)++bin[rk[i]];
for(int i=1;i<sz;i++)bin[i]+=bin[i-1];
for(int i=n-1;i>=0;i--)sa[--bin[rk[tmp[i]]]]=tmp[i];
p=0;
tmp[sa[0]]=0;
for(int i=1;i<n;i++){
int v0=sa[i-1],v1=sa[i],v00,v01;
if(v0+j<n)v00=rk[v0+j];else v00=-1;
if(v1+j<n)v01=rk[v1+j];else v01=-1;
if(rk[sa[i-1]]==rk[sa[i]]&&v00==v01)tmp[sa[i]]=p;
else tmp[sa[i]]=++p;
}
for(int i=0;i<n;i++)rk[i]=tmp[i];
}
}
int h[maxn];
int calc(int x,int y,int res){
for(;s[x+res]==s[y+res];)++res;
return res;
}
void geth(){
if(rk[0]==0)h[0]=0;
else h[0]=calc(0,sa[rk[0]-1],0);
for(int i=1;i<n;i++){
if(rk[i]==0)h[i]=0;
else h[i]=calc(i,sa[rk[i]-1],max(h[i-1]-1,0));
}
}
void solve(){
scanf("%s",s);
n=strlen(s);
getsa();
geth();
int ans=0;
for(int i=0;i<n;i++){
int curh=n-sa[i];
ans+=curh-h[sa[i]];
}
printf("%d\n",ans);
}
int main(){
int T;
scanf("%d",&T);
while(T--)solve();
return 0;
}
BZOJ1717
题目链接:https://lydsy.com/JudgeOnline/problem.php?id=1717
题解:
题目首先可以转化为:计算出现了至少
k
k
k次的最长子串
有两种方法,首先都需要求出
s
a
sa
sa和
h
h
h数组
1)在
h
[
s
a
[
i
]
]
h[sa[i]]
h[sa[i]]上取连续
k
−
1
k-1
k−1个数的最小值,统计出来之后求最大值即可,可以用st表维护,正确性显然
2)二分,将
h
[
s
a
[
i
]
]
<
m
i
d
h[sa[i]]<mid
h[sa[i]]<mid的
i
i
i断开,看是否有一段区间至少有连续的
k
k
k个元素
两者时间复杂度皆为
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)
两种方法实际表现经实测基本相同
1)的实现:
// by Balloons
#include <cstdio>
#include <cstring>
#include <iostream>
#include <cmath>
#include <algorithm>
#define mpr make_pair
#define debug() puts("okkkkkkkk")
#define rep(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
using namespace std;
typedef long long LL;
const int inf = 1 << 30;
const int maxn=1e6+5;
int n,k;
int a[100005];
int rk[100005],sa[100005],bin[maxn],tmp[100005];
void getsa(){
int sz=1e6+1;
for(int i=0;i<n;i++)rk[i]=a[i];
for(int i=0;i<sz;i++)bin[i]=0;
for(int i=0;i<n;i++)++bin[rk[i]];
for(int i=1;i<sz;i++)bin[i]+=bin[i-1];
for(int i=n-1;i>=0;i--)sa[--bin[rk[i]]]=i;
for(int j=1;j<=n;j<<=1){
int p=0;
for(int i=n-j;i<n;i++)tmp[p++]=i;
for(int i=0;i<n;i++)if(sa[i]-j>=0)tmp[p++]=sa[i]-j;
for(int i=0;i<sz;i++)bin[i]=0;
for(int i=0;i<n;i++)++bin[rk[i]];
for(int i=1;i<sz;i++)bin[i]+=bin[i-1];
for(int i=n-1;i>=0;i--)sa[--bin[rk[tmp[i]]]]=tmp[i];
p=0;tmp[sa[0]]=0;
for(int i=1;i<n;i++){
int v0=sa[i-1],v1=sa[i],v00,v01;
if(v0+j<n)v00=rk[v0+j];else v00=-1;
if(v1+j<n)v01=rk[v1+j];else v01=-1;
if(rk[sa[i-1]]==rk[sa[i]]&&v00==v01)tmp[sa[i]]=p;
else tmp[sa[i]]=++p;
}
for(int i=0;i<n;i++)rk[i]=tmp[i];
}
}
int h[maxn];
int calc(int x,int y,int res){
for(;a[x+res]==a[y+res];)++res;
return res;
}
void geth(){
if(rk[0]==0)h[0]=0;else h[0]=calc(0,sa[rk[0]-1],0);
for(int i=1;i<n;i++){
if(rk[i]==0)h[i]=0;
else h[i]=calc(i,sa[rk[i]-1],max(h[i-1]-1,0));
}
}
int st[20005][24];
int ta[maxn];
void lsh(){
memcpy(ta,a,sizeof a);
sort(ta,ta+n);
for(int i=0;i<n;i++)a[i]=lower_bound(ta,ta+n,a[i])-ta;
for(int i=0;i<n;i++)++a[i];
}
int query(int x,int y){
int k=(int)(1.0*log(y-x+1)/log(2.0));
return min(st[x][k],st[y-(1<<k)+1][k]);
}
int main(){
scanf("%d%d",&n,&k);
for(int i=0;i<n;i++)scanf("%d",&a[i]);
lsh();
getsa();
geth();
// for(int i=0;i<n;i++)printf("%d ",sa[i]+1);puts("");
// for(int i=0;i<n;i++)printf("%d ",h[sa[i]]);
// puts("");
for(int i=1;i<=n;i++)st[i][0]=h[sa[i-1]];
for(int j=1;j<=20;j++)
for(int i=1;i+(1<<j)-1<=n;i++)st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1]);
int ans=0;
for(int i=1;i+k-2<=n;i++)ans=max(ans,query(i,i+k-2));
printf("%d\n",ans);
return 0;
}
2)的代码:
// by Balloons
#include <cstdio>
#include <cstring>
#include <iostream>
#include <cmath>
#include <algorithm>
#define mpr make_pair
#define debug() puts("okkkkkkkk")
#define rep(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
using namespace std;
typedef long long LL;
const int inf = 1 << 30;
const int maxn=1e6+5;
int n,k;
int a[100005];
int rk[100005],sa[100005],bin[maxn],tmp[100005];
void getsa(){
int sz=1e6+1;
for(int i=0;i<n;i++)rk[i]=a[i];
for(int i=0;i<sz;i++)bin[i]=0;
for(int i=0;i<n;i++)++bin[rk[i]];
for(int i=1;i<sz;i++)bin[i]+=bin[i-1];
for(int i=n-1;i>=0;i--)sa[--bin[rk[i]]]=i;
for(int j=1;j<=n;j<<=1){
int p=0;
for(int i=n-j;i<n;i++)tmp[p++]=i;
for(int i=0;i<n;i++)if(sa[i]-j>=0)tmp[p++]=sa[i]-j;
for(int i=0;i<sz;i++)bin[i]=0;
for(int i=0;i<n;i++)++bin[rk[i]];
for(int i=1;i<sz;i++)bin[i]+=bin[i-1];
for(int i=n-1;i>=0;i--)sa[--bin[rk[tmp[i]]]]=tmp[i];
p=0;tmp[sa[0]]=0;
for(int i=1;i<n;i++){
int v0=sa[i-1],v1=sa[i],v00,v01;
if(v0+j<n)v00=rk[v0+j];else v00=-1;
if(v1+j<n)v01=rk[v1+j];else v01=-1;
if(rk[sa[i-1]]==rk[sa[i]]&&v00==v01)tmp[sa[i]]=p;
else tmp[sa[i]]=++p;
}
for(int i=0;i<n;i++)rk[i]=tmp[i];
}
}
int h[maxn];
int calc(int x,int y,int res){
for(;a[x+res]==a[y+res];)++res;
return res;
}
void geth(){
if(rk[0]==0)h[0]=0;else h[0]=calc(0,sa[rk[0]-1],0);
for(int i=1;i<n;i++){
if(rk[i]==0)h[i]=0;
else h[i]=calc(i,sa[rk[i]-1],max(h[i-1]-1,0));
}
}
int st[20005][24];
int ta[maxn],height[maxn];
void lsh(){
memcpy(ta,a,sizeof a);
sort(ta,ta+n);
for(int i=0;i<n;i++)a[i]=lower_bound(ta,ta+n,a[i])-ta;
for(int i=0;i<n;i++)++a[i];
}
int check(int x){
for(int i=2;i<=n;i++){
int j;
for(;height[i]<x&&i<=n;i++);
for(j=i;height[j]>=x;j++);
j--;
if((j-i+1)+1>=k)return 1; // 额外的那个1是因为 height 是相邻的两个,所以j-i+1 相当于有j-i+1+1个数
}
return 0;
}
int main(){
scanf("%d%d",&n,&k);
for(int i=0;i<n;i++)scanf("%d",&a[i]);
lsh();
getsa();
geth();
for(int i=1;i<=n;i++)height[i]=h[sa[i-1]];
int l=1,r=n,ans;
while(l<=r){
int mid=l+r>>1;
// printf("mid=%d, isok=%d\n",mid,check(mid));
if(check(mid)){ans=mid;l=mid+1;}
else r=mid-1;
}
printf("%d\n",ans);
return 0;
}
BZOJ1031
题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=1031
题解:板子题,将字符串复制一遍,求一发 s a sa sa之后看 s a [ i ] sa[i] sa[i]对应的尾字符即可
代码:
// by Balloons
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define mpr make_pair
#define debug() puts("okkkkkkkk")
#define rep(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
using namespace std;
typedef long long LL;
const int inf = 1 << 30;
const int maxn=2e5+5;
char s[maxn],t[maxn];
int rk[maxn],sa[maxn],bin[maxn],tmp[maxn];
int n;
void getsa(){
n=strlen(s);
int sz=max(n,666);
for(int i=0;i<n;i++)rk[i]=s[i];
for(int i=0;i<sz;i++)bin[i]=0;
for(int i=0;i<n;i++)++bin[rk[i]];
for(int i=1;i<sz;i++)bin[i]+=bin[i-1];
for(int i=n-1;i>=0;i--)sa[--bin[rk[i]]]=i;
for(int j=1;j<=n;j<<=1){
int p=0;
for(int i=n-j;i<n;i++)tmp[p++]=i;
for(int i=0;i<n;i++)if(sa[i]-j>=0)tmp[p++]=sa[i]-j;
for(int i=0;i<sz;i++)bin[i]=0;
for(int i=0;i<n;i++)++bin[rk[i]];
for(int i=1;i<sz;i++)bin[i]+=bin[i-1];
for(int i=n-1;i>=0;i--)sa[--bin[rk[tmp[i]]]]=tmp[i];
p=0;tmp[sa[0]]=0;
for(int i=1;i<n;i++){
int v0=sa[i-1],v1=sa[i],v00,v01;
if(v0+j<n)v00=rk[v0+j];else v00=-1;
if(v1+j<n)v01=rk[v1+j];else v01=-1;
if(rk[sa[i-1]]==rk[sa[i]]&&v00==v01)tmp[sa[i]]=p;
else tmp[sa[i]]=++p;
}
for(int i=0;i<n;i++)rk[i]=tmp[i];
}
}
int main(){
scanf("%s",t);
sprintf(s,"%s%s",t,t);
// sprintf(s,"%s",t);
getsa();
int nt=strlen(t);
for(int i=0;i<n;i++){
if(sa[i]<nt)
printf("%c",s[sa[i]+nt-1]); // sa[i] 是 字符串的开头,要得到结尾
}
puts("");
return 0;
}
POJ1226
题目链接:http://poj.org/problem?id=1226
题解:
首先将每个字符串正反拼起来,每个字符串之间用一个未出现过的字符连接
跑一遍后缀数组后考虑二分
二分
l
e
n
len
len为最长的长度,每次看连续的一段
h
e
i
g
h
t
height
height是否存在
≥
n
\ge n
≥n的长度
l
c
p
lcp
lcp都为
≥
l
e
n
\ge len
≥len
如何判断两个字符串是同一字符串翻转得到的呢?
可以开一个
s
e
t
set
set记录当前得到的字符串都是属于哪个字符串翻转或不翻转得到的,这样在预处理的时候维护一个
i
d
x
[
i
]
idx[i]
idx[i]表示拼起来之后的大字符串
s
s
s中的
s
[
i
]
s[i]
s[i]是在第几个字符串中的就ok啦
有1个字符串的时候需要特判一下
PS:手抖把一个变量开到全局去了,因此RE 10次 QAQ
代码:
// by Balloons
#include <cstdio>
#include <cstring>
#include <iostream>
#include <set>
#include <algorithm>
#define mpr make_pair
#define debug() puts("okkkkkkkk")
#define rep(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
using namespace std;
typedef long long LL;
const int inf = 1 << 30;
const int maxn=5e4+5;
char tmps[maxn];
int s[maxn];
int rk[maxn],sa[maxn],bin[maxn],tmp[maxn],idx[maxn];
int n,num;
void getsa(){
int sz=max(n,300);
for(int i=0;i<n;i++)rk[i]=s[i]-'A';
for(int i=0;i<sz;i++)bin[i]=0;
for(int i=0;i<n;i++)++bin[rk[i]];
for(int i=1;i<sz;i++)bin[i]+=bin[i-1];
for(int i=n-1;i>=0;i--)sa[--bin[rk[i]]]=i;
for(int j=1;j<=n;j<<=1){
int p=0;
for(int i=n-j;i<n;i++)tmp[p++]=i;
for(int i=0;i<n;i++)if(sa[i]-j>=0)tmp[p++]=sa[i]-j;
for(int i=0;i<sz;i++)bin[i]=0;
for(int i=0;i<n;i++)++bin[rk[i]];
for(int i=1;i<sz;i++)bin[i]+=bin[i-1];
for(int i=n-1;i>=0;i--)sa[--bin[rk[tmp[i]]]]=tmp[i];
p=0;tmp[sa[0]]=0;
for(int i=1;i<n;i++){
int v0=sa[i-1],v1=sa[i],v00,v01;
if(v0+j<n)v00=rk[v0+j];else v00=-1;
if(v1+j<n)v01=rk[v1+j];else v01=-1;
if(rk[sa[i-1]]==rk[sa[i]]&&v00==v01)tmp[sa[i]]=p;
else tmp[sa[i]]=++p;
}
for(int i=0;i<n;i++)rk[i]=tmp[i];
}
}
int h[maxn];
int calc(int x,int y,int res){
for(;s[x+res]==s[y+res];)++res;
return res;
}
void geth(){
if(rk[0]==0)h[0]=0;else h[0]=calc(0,sa[rk[0]-1],0);
for(int i=1;i<n;i++){
if(rk[i]==0)h[i]=0;
else h[i]=calc(i,sa[rk[i]-1],max(h[i-1]-1,0));
}
}
int check(int x){
set<int>S;
for(int i=1;i<n;i++){
if(h[sa[i]]>=x){
S.insert(idx[sa[i-1]]);S.insert(idx[sa[i]]);
}else{
if(S.size()==num)return 1;
S.clear();
}
}
if(S.size()==num)return 1;
return 0;
}
void solve(){
n=0;memset(s,0,sizeof s);
scanf("%d",&num);
if(num==1){
scanf("%s",tmps);
printf("%d\n",strlen(tmps));
return ;
}
int delta=0;
for(int i=1;i<=num;i++){
scanf("%s",tmps);
int tn=strlen(tmps);
for(int j=0;j<tn;j++){
idx[n]=i;
s[n++]=tmps[j]+2*num+1;
}
idx[n]=i;
s[n++]=delta+'A';++delta;
for(int j=tn-1;j>=0;j--){
idx[n]=i;
s[n++]=tmps[j]+2*num+1;
}
idx[n]=i;
s[n++]=delta+'A';++delta;
}
getsa();
geth();
int l=1,r=100,ans=0;
while(l<=r){
int mid=l+r>>1;
if(check(mid)){ans=mid;l=mid+1;}
else r=mid-1;
}
printf("%d\n",ans);
}
int main(){
int T;scanf("%d",&T);
while(T--)solve();
return 0;
}
BZOJ3238
题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=3238
题解:前两项找找规律就行了,是
(
n
−
1
)
∗
n
∗
(
n
+
1
)
/
2
(n-1)*n*(n+1)/2
(n−1)∗n∗(n+1)/2,主要是处理第3项
求出
h
e
i
g
h
t
height
height之后答案就变成了
2
∗
∑
i
,
j
m
i
n
(
h
e
i
g
h
t
i
+
1
.
.
.
j
)
2*\sum_{i,j}min(height_{{i+1}...j})
2∗i,j∑min(heighti+1...j)这个可以用单调栈维护,其中单调栈
s
t
[
t
o
p
]
st[top]
st[top]是一个递增的栈,表示
h
e
i
g
h
t
[
i
]
.
.
.
h
e
i
g
h
t
[
n
]
height[i]...height[n]
height[i]...height[n]最小值是多少每次插入一个数的时候要弹掉不满足单调性的元素,这里面最小值也会发生改变:
所以每次弹栈的时候需要减掉的是[A,B]这一段、[i,A]这一段(只是个比方,可能有很多段都需要减)这几段的最小值应是B而不是A,具体参考代码
还有一个就是代码中记录减的这个变量
t
m
p
tmp
tmp不应该每次都初始化,只应该初始化一次。
原因:
t
m
p
tmp
tmp记录的是更改之后的值,如果每次都初始化的话,相当于上图的A抹掉了之后对后面无法产生影响,有
t
m
p
tmp
tmp记录的是A抹掉时变化的量
代码:
// by Balloons
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define mpr make_pair
#define debug() puts("okkkkkkkk")
#define rep(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
using namespace std;
typedef long long LL;
#define int LL
const int inf = 1 << 30;
const int maxn=1e6+5;
char s[maxn];
int n,sz;
int h[maxn],sa[maxn],rk[maxn],tmp[maxn],bin[maxn];
int height[maxn],st[maxn];
void getsa(){
sz=max(n,27ll);
for(int i=0;i<n;i++)rk[i]=s[i]-'a';
for(int i=0;i<sz;i++)bin[i]=0;
for(int i=0;i<n;i++)++bin[rk[i]];
for(int i=1;i<sz;i++)bin[i]+=bin[i-1];
for(int i=n-1;i>=0;i--)sa[--bin[rk[i]]]=i;
for(int j=1;j<=n;j<<=1){
int p=0;
for(int i=n-j;i<n;i++)tmp[p++]=i;
for(int i=0;i<n;i++)if(sa[i]-j>=0)tmp[p++]=sa[i]-j;
for(int i=0;i<sz;i++)bin[i]=0;
for(int i=0;i<n;i++)++bin[rk[i]];
for(int i=1;i<sz;i++)bin[i]+=bin[i-1];
for(int i=n-1;i>=0;i--)sa[--bin[rk[tmp[i]]]]=tmp[i];
p=0;tmp[sa[0]]=0;
for(int i=1;i<n;i++){
int v0=sa[i-1],v1=sa[i],v00,v01;
if(v0+j<n)v00=rk[v0+j];else v00=-1;
if(v1+j<n)v01=rk[v1+j];else v01=-1;
if(rk[v0]==rk[v1]&&v00==v01)tmp[sa[i]]=p;
else tmp[sa[i]]=++p;
}
for(int i=0;i<n;i++)rk[i]=tmp[i];
}
}
int calc(int x,int y,int res){
for(;s[x+res]==s[y+res];)++res;
return res;
}
void geth(){
if(rk[0]==0)h[0]=0;else h[0]=calc(0,sa[rk[0]-1],0);
for(int i=1;i<n;i++){
if(rk[i]==0)h[i]=0;
else h[i]=calc(i,sa[rk[i]-1],max(h[i-1]-1,0ll));
}
}
signed main(){
scanf("%s",s);
n=strlen(s);
getsa();
// for(int i=0;i<n;i++)printf("%d ",sa[i]+1);puts("");
geth();
// for(int i=1;i<n;i++)printf("%d ",h[sa[i]]);puts("");
LL ans=(n-1)*n*(n+1)/2,top=0;
for(int i=1;i<=n;i++)height[i]=h[sa[i-1]];
height[0]=-inf;st[0]=0;
LL tmp=0; // tmp变量
for(int i=1;i<=n;i++){
while(height[st[top]]>height[i]){ // > 和 >= 均可以
tmp-=(st[top]-st[top-1])*height[st[top]];
--top;
}
tmp+=(i-st[top])*height[i];st[++top]=i;
ans-=2*tmp;
}
printf("%lld\n",ans);
return 0;
}