【题目描述】
给定一个字符串,计算其不同的子串个数。
【输入格式】
一行一个仅包含大写字母的字符串,长度<=50000
【输出格式】
一行一个正整数,即不同的子串个数。
【样例输入】
ABABA
【样例输出】
9
可以用后缀数组求出每个后缀的height,然后由于height为当前后缀与前边后缀最长公共前缀的最小值,所以当前后缀的不同子串数为该后缀长度减去该后缀的heght,最后求和即可
代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=50000+100;
char s[maxn];
int sa[maxn],t[maxn],t2[maxn],c[maxn];
inline int idx(char c){
return c-'A';
}
inline void build_sa(int n,int m){
int *x=t,*y=t2;
for(int i=0;i<m;i++)
c[i]=0;
for(int i=0;i<n;i++)
c[x[i]=idx(s[i])]++;
for(int i=1;i<m;i++)
c[i]+=c[i-1];
for(int i=n-1;i>=0;i--)
sa[--c[x[i]]]=i;
for(int k=1;k<n;k<<=1){
int p=0;
for(int i=n-k;i<n;i++)
y[p++]=i;
for(int i=0;i<n;i++)
if(sa[i]>=k)
y[p++]=sa[i]-k;
for(int i=0;i<m;i++)
c[i]=0;
for(int i=0;i<n;i++)
c[x[y[i]]]++;
for(int i=1;i<m;i++)
c[i]+=c[i-1];
for(int i=n-1;i>=0;i--)
sa[--c[x[y[i]]]]=y[i];
swap(x,y);
p=1;
x[sa[0]]=0;
for(int i=1;i<n;i++){
x[sa[i]]=(y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+k]==y[sa[i]+k])?p-1:p++;
}
if(p>=n)
break;
m=p;
}
}
int rank[maxn],height[maxn];
inline void getheight(int n){
int h=0;
for(int i=0;i<n;i++)
rank[sa[i]]=i;
for(int i=0;i<n;i++){
if(rank[i]==0) h=0;
else{
int k=sa[rank[i]-1];
if(--h<0)
h=0;
while(s[i+h]==s[k+h])
h++;
}
height[rank[i]]=h;
}
}
int main(){
freopen("subst1.in","r",stdin);
freopen("subst1.out","w",stdout);
scanf("%s",s);
int n=strlen(s);
s[n]='A'+29;
build_sa(n+1,30);
getheight(n);
long long ans=0;
for(int i=0;i<n;i++)
ans+=n-sa[i]-height[i];
printf("%lld\n",ans);
return 0;
}
后缀数组求不同子串数
1025

被折叠的 条评论
为什么被折叠?



