P2408 不同子串個數(SA-LCP)
經典的題目。
顯然所有子串數目爲。
因此我們只需知道重複的子串有多少個。
根據我們知道利用求出的,他們的長度即是重複的子串個數,這樣求的正確性是因爲要找到重複的子串,首先兩個子串下標必須不一樣,且長度一樣,字母完全一樣,所以根據排名第名和第名是字典序最接近的可以知道求代表重複子串個數,求和便可得到重複子串總數。
所以
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=1e6+5;
char s[N];
int n,sz,sa[N],rk[N],tp[N],b[N],ht[N];
typedef long long ll;
void Qsort(){ //基數排序.
for(int i=0;i<=sz;i++) b[i]=0;
for(int i=1;i<=n;i++) b[rk[i]]++;
for(int i=1;i<=sz;i++) b[i]+=b[i-1];
for(int i=n;i>=1;i--) sa[b[rk[tp[i]]]--]=tp[i];
}
void SA(){
sz=80;
for(int i=1;i<=n;i++) rk[i]=s[i]-'0'+1,tp[i]=i;
Qsort();
for(int w=1,id=0;id<n;sz=id,w<<=1){
id=0;
for(int i=n-w+1;i<=n;i++) tp[++id]=i;
for(int i=1;i<=n;i++) if(sa[i]>w) tp[++id]=sa[i]-w;
Qsort();
swap(rk,tp);
rk[sa[1]]=id=1;
for(int i=2;i<=n;i++)
rk[sa[i]]= (tp[sa[i]]==tp[sa[i-1]]&&tp[sa[i]+w]==tp[sa[i-1]+w])?id:++id;
}
}
void GetHight(){ //求height
int k=0;
for(int i=1;i<=n;i++){
if(k) k--;
int j=sa[rk[i]-1];
while(s[i+k]==s[j+k]) k++;
ht[rk[i]]=k;
}
}
int main(){
scanf("%d%s",&n,s+1);
SA();
GetHight();
ll ans=1LL*n*(n+1)/2;
for(int i=1;i<=n;i++) ans-=ht[i];
printf("%lld\n",ans);
return 0;
}