题解 P2408 【不同子串个数】
统计一个串不同的子串个数:
我们可以利用
根据定义,后缀
这两个串的前缀,就是原字符串的子串,
那么这两个串产生的相同的子串,数目就是
所有后缀产生的前缀,相同的个数是
一个长度为
最后答案为总个数减去相同的个数,即:
完整代码奉上:
#include<iostream>
#include<cstdio>
#include<ctype.h>
#include<cstring>
#include<queue>
using namespace std;
inline int read(){
int x=0,f=0;char ch=getchar();
while(!isdigit(ch))f|=ch=='-',ch=getchar();
while(isdigit(ch))x=x*10+(ch^48),ch=getchar();
return f?-x:x;
}
char s[100007];
int N,M;
int rak[100007],sa[100007],tp[100007],tax[100007];
inline void bucket(){
for(int i=0;i<=M;++i)tax[i]=0;
for(int i=1;i<=N;++i)tax[rak[i]]++;
for(int i=1;i<=M;++i)tax[i]+=tax[i-1];
for(int i=N;i>=1;--i)sa[tax[rak[tp[i]]]--]=tp[i];
}
inline void suffix_sort(){//SA模板
M=128;
for(int i=1;i<=N;++i)rak[i]=s[i]-'a'+1,tp[i]=i;
bucket();
for(int w=1,p=0;p<N;M=p,w<<=1){
p=0;
for(int i=1;i<=w;++i)tp[++p]=N-w+i;
for(int i=1;i<=N;++i)if(sa[i]>w)tp[++p]=sa[i]-w;
bucket();
swap(tp,rak);
rak[sa[1]]=p=1;
for(int i=2;i<=N;++i)
rak[sa[i]]=(tp[sa[i-1]]==tp[sa[i]] && tp[sa[i-1]+w]==tp[sa[i]+w])?p:++p;
}
}
int height[100007];
inline void Get_Height(){//求Height数组
int k=0;
for(int i=1;i<=N;++i){
if(k)k--;
int j=sa[rak[i]-1];
while(s[i+k]==s[j+k])++k;
height[rak[i]]=k;
}
}
int main(){
N=read();scanf("%s",s+1);
suffix_sort();
Get_Height();
long long ans=1ll*N*(N+1)/2;//总子串个数
for(int i=1;i<=N;++i)ans-=height[rak[i]];//减去相同的个数
printf("%lld\n",ans);
return 0;//好习惯..
}