您的位置:首页 > 其它

spoj694 Distinct Substrings(后缀数组+统计不同子串的个数)

2015-09-14 20:08 309 查看
给定一个字符串,求不相同的子串的个数。

算法分析:

每个子串一定是某个后缀的前缀,那么原问题等价于求所有后缀之间的不相

同的前缀的个数。如果所有的后缀按照 suffix(sa[1]), suffix(sa[2]),

suffix(sa[3]), …… ,suffix(sa
)的顺序计算,不难发现,对于每一次新加

进来的后缀 suffix(sa[k]),它将产生 n-sa[k]+1 个新的前缀。但是其中有

height[k]个是和前面的字符串的前缀是相同的。所以 suffix(sa[k])将“贡献”

出 n-sa[k]+1- height[k]个不同的子串。累加后便是原问题的答案。这个做法

的时间复杂度为 O(n)。

#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
using namespace std;
#define LL long long
#define cl(a,b) memset(a,b,size(a))
const int maxn=500002;

int n,k;
int rank[maxn],tmp[maxn],sa[maxn],lcp[maxn];
char s[maxn];
int cmp(int i,int j){
if(rank[i]!=rank[j])return rank[i]<rank[j];
int ri=i+k<=n?rank[i+k]:-1;
int rj=j+k<=n?rank[j+k]:-1;
return ri<rj;
}
void getSa(){
n=strlen(s);
for(int i=0;i<=n;i++){
sa[i]=i;
rank[i]=i<n?s[i]:-1;
}
for(k=1;k<=n;k<<=1){
sort(sa,sa+n+1,cmp);
tmp[sa[0]]=0;
for(int i=1;i<=n;i++){
tmp[sa[i]]=tmp[sa[i-1]]+cmp(sa[i-1],sa[i]);
}
for(int i=0;i<=n;i++)rank[i]=tmp[i];
}
}
void getLcp(){
// n=strlen(s);
for(int i=0;i<=n;i++)rank[sa[i]]=i;
int h=0;
lcp[0]=0;
for(int i=0;i<n;i++){
int j=sa[rank[i]-1];
if(h>0)h--;
while(i+h<n&&j+h<n&&s[i+h]==s[j+h])h++;
lcp[rank[i]-1]=h;
}
}
int main(){
int T;
scanf("%d",&T);
while(T--){
scanf("%s",s);
getSa();getLcp();
int ans=n-sa[0];
for(int i=1;i<=n;i++){
ans+=n-sa[i]-lcp[i-1];
}
printf("%d\n",ans);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: