题意:
求一个字符串有多少本质不同的子串,这里的不同指两个子串的最小表示法不同.
数据范围:
n < = 5 e 4 , 1 < = a [ i ] < = n , 其 中 n 是 字 符 串 长 度 , a [ i ] 是 字 符 n<=5e4,1<=a[i]<=n,其中n是字符串长度,a[i]是字符 n<=5e4,1<=a[i]<=n,其中n是字符串长度,a[i]是字符
首先,如果不同指字符串本身不同,那么有经典做法:先后缀排序,求出排名相邻的后缀的lcp,然后就等于总字符串个数-所有相邻后缀的lcp的长度.
但是现在不同的定义不同,我们考虑更改hash的定义,变成"每个位置到下一个与其相同的位置的距离"
∗
w
i
*w^i
∗wi,其中
w
w
w是步长,然后就可以用线段树维护一个子串的hash值了,就可以二分+hash求lcp
#include<bits/stdc++.h>
using namespace std;
const int maxn=5e4+5;
typedef unsigned long long ull;
const ull step=233;
inline int read(){
char c=getchar();int t=0,f=1;
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){t=(t<<3)+(t<<1)+(c^48);c=getchar();}
return t*f;
}
int n,a[maxn],rt[maxn],lst[maxn],p[maxn],ord[maxn];
ull pw[maxn];
ull sum[maxn<<5],ha;
int ls[maxn<<5],rs[maxn<<5];
map<int,ull> h[maxn];
int cnt=0;
void modify(int &now,int l,int r,int x,int val){
int e=++cnt;
ls[e]=ls[now];rs[e]=rs[now];now=e;
if(l==r){sum[now]=val;return ;}
int mid=l+r>>1;
if(x<=mid)modify(ls[now],l,mid,x,val);
else modify(rs[now],mid+1,r,x,val);
sum[now]=sum[ls[now]]*pw[r-mid]+sum[rs[now]];
}
void query(int now,int l,int r,int x,int y){
if(!now){
int len=min(y,r)-max(x,l)+1;
ha=ha*pw[len];
return ;
}
if(x<=l&&r<=y){
int len=r-l+1;
ha=ha*pw[len]+sum[now];
return ;
}
int mid=l+r>>1;
if(x<=mid)query(ls[now],l,mid,x,y);
if(y>mid)query(rs[now],mid+1,r,x,y);
return ;
}
bool same(int l,int r,int len){
ull t1,t2;
if(h[l].count(l+len-1))t1=h[l][l+len-1];
else {ha=0;query(rt[l],1,n,l,l+len-1);h[l][l+len-1]=t1=ha;}
if(h[r].count(r+len-1))t2=h[r][r+len-1];
else {ha=0;query(rt[r],1,n,r,r+len-1);h[r][r+len-1]=t2=ha;}
return t1==t2;
}
int lcp(int x,int y){
if(x>y)swap(x,y);
int p=0;
for(int i=15;i>=0;i--)if(y+p+(1<<i)-1<=n&&same(x,y,p+(1<<i)))p+=1<<i;
return p;
}
bool cmp(int x,int y){
int z=lcp(x,y);
if(x+z>n)return 1;
if(y+z>n)return 0;
int t1=z<p[x+z]?0:p[x+z],t2=z<p[y+z]?0:p[y+z];
return t1<t2;
}
int main(){
//freopen("c.in","r",stdin);
//freopen("c2.out","w",stdout);
n=read();
for(int i=1;i<=n;i++)a[i]=read();
pw[0]=1;
for(int i=1;i<=n;i++)pw[i]=pw[i-1]*step;
for(int i=n;i>=1;i--){rt[i]=rt[i+1];
if(lst[a[i]]){
modify(rt[i],1,n,lst[a[i]],lst[a[i]]-i);
p[lst[a[i]]]=lst[a[i]]-i;
}
lst[a[i]]=i;
}
long long ans=0;
for(int i=1;i<=n;i++)ord[i]=i;
stable_sort(ord+1,ord+1+n,cmp);
for(int i=1;i<=n;i++)ans=ans+n-i+1;
for(int i=2;i<=n;i++)ans-=lcp(ord[i-1],ord[i]);
printf("%lld\n",ans);
return 0;
}