​题目传送门 - Codeforces 802I​​题意

  求一个串中,所有本质不同子串的出现次数的平方和。

  $|s|\leq 10^5$

题解

  首先,这一题用 SAM 做就是模板题,比较简单。

  但是,本着练一练 SA 的心态,我开始了 SA+单调栈 的苦海。

  真毒瘤。

  这里讲一讲 SA 的做法,也是经典的做法。

   SA 闭着眼睛先写了再说。

  首先,我们考虑出现次数大于 $1$ 次的子串。

  考虑按照$SA$数组的顺序来进行处理,这样得到的后缀的字典序不断变大。

  如果要统计一个串与前一个串的 LCP 出现了多少次,该如何统计?

  显然是往前和往后都找到第一个 LCP 比当前小的停止并统计。

  于是我们用单调栈来维护一个 $height$ 升序的序列。具体的统计方法这里不多赘述,可以直接查阅代码。比较明了。

  单调栈要注意处理当前 LCP 和栈顶 LCP 长度值相同的情况。

  考虑只出现一次的串个数。对于第 $i$ 大的后缀(即 $SA[i]$ ),之前统计到的是 $SA[i]$ 与 $SA[i-1]$ 、$SA[i]$ 与 $SA[i+1] $ 的 LCP 的 $\max$ 。于是没用统计到的就是剩下的。于是当前后缀出现依次的串对答案的贡献就是 $len-\max$ 。

代码



#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=100005;
int T,n,m,SA[N],rank[N],height[N],tmp[N],tax[N];
char s[N];
int st[N],top,pos[N];
void Sort(int n,int m){
for (int i=0;i<=m;i++)
tax[i]=0;
for (int i=1;i<=n;i++)
tax[rank[i]]++;
for (int i=1;i<=m;i++)
tax[i]+=tax[i-1];
for (int i=n;i>=1;i--)
SA[tax[rank[tmp[i]]]--]=tmp[i];
}
bool cmp(int rk[],int x,int y,int w){
return rk[x]==rk[y]&&rk[x+w]==rk[y+w];
}
void Suffix_Array(char s[],int n){
memset(SA,0,sizeof SA);
memset(tmp,0,sizeof tmp);
memset(rank,0,sizeof rank);
memset(height,0,sizeof height);
for (int i=1;i<=n;i++)
rank[i]=s[i],tmp[i]=i;
m=127;
Sort(n,m);
for (int w=1,p=0;p<n;w<<=1,m=p){
p=0;
for (int i=n-w+1;i<=n;i++)
tmp[++p]=i;
for (int i=1;i<=n;i++)
if (SA[i]>w)
tmp[++p]=SA[i]-w;
Sort(n,m);
swap(rank,tmp);
rank[SA[1]]=p=1;
for (int i=2;i<=n;i++)
rank[SA[i]]=cmp(tmp,SA[i],SA[i-1],w)?p:++p;
}
for (int i=1,j,k=0;i<=n;height[rank[i++]]=k)
for (k=max(k-1,0),j=SA[rank[i]-1];s[i+k]==s[j+k];k++);
}
int main(){
scanf("%d",&T);
while (T--){
scanf("%s",s+1);
n=strlen(s+1);
Suffix_Array(s,n);
LL ans=0;
top=0;
memset(st,0,sizeof st);
memset(pos,0,sizeof pos);
SA[n+1]=height[0]=0;
for (int i=2;i<=n+1;i++){
int nowpos=i,len=height[i];
while (top>0&&st[top]>len){
LL v1=st[top]-max(st[top-1],len);
LL v2=i-pos[top]+1;
ans+=v1*v2*v2;
nowpos=pos[top--];
}
while (top>0&&st[top]==len)
nowpos=pos[top--];
st[++top]=len;
pos[top]=nowpos;
}
for (int i=1;i<=n;i++)
ans+=n-i+1-max(height[rank[i]],height[rank[i]+1]);
printf("%I64d\n",ans);
}
return 0;
}