SPOJ 694/705 后缀数组

题意:

求单个子串的不重复子串个数

 

题解:

一个字符串中的所有子串都必然是它的后缀的前缀。

对于每一个sa[i]后缀,它的起始位置sa[i],那么它最多能得到该后缀长度个子串(n-sa[i]个),而其中有height[i]个是与前一个后缀相同的,所以它能产生的实际后缀个数便是n-sa[i]-height[i]。遍历一次所有的后缀,将它产生的后缀数加起来便是答案。

 

View Code
 1 #include <iostream>
 2 #include <cstring>
 3 #include <cstdlib>
 4 #include <cstdio>
 5 #include <algorithm>
 6 
 7 #define N 50050
 8 
 9 using namespace std;
10 
11 int wa[N],wb[N],wc[N],wv[N];
12 int r[N],sa[N];
13 char str[N];
14 int rank[N],height[N];
15 
16 inline bool cmp(int *r,int a,int b,int l)
17 {
18     return r[a]==r[b]&&r[a+l]==r[b+l];
19 }
20 
21 inline void da(int *r,int *sa,int n,int m)
22 {
23     int i,j,p,*x=wa,*y=wb,*t;
24     for(i=0;i<m;i++) wc[i]=0;
25     for(i=0;i<n;i++) wc[x[i]=r[i]]++;
26     for(i=1;i<m;i++) wc[i]+=wc[i-1];
27     for(i=n-1;i>=0;i--) sa[--wc[x[i]]]=i;
28     for(j=1,p=1;p<n;j<<=1,m=p)
29     {
30         for(i=n-j,p=0;i<n;i++) y[p++]=i;
31         for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j;
32         for(i=0;i<n;i++) wv[i]=x[y[i]];
33         for(i=0;i<m;i++) wc[i]=0;
34         for(i=0;i<n;i++) wc[wv[i]]++;
35         for(i=1;i<m;i++) wc[i]+=wc[i-1];
36         for(i=n-1;i>=0;i--) sa[--wc[wv[i]]]=y[i];
37         for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1;i<n;i++)
38             x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
39     }
40 }
41 
42 inline void getheight(int *r,int *sa,int n)
43 {
44     int i,j,k=0;
45     for(i=1;i<=n;i++) rank[sa[i]]=i;
46     for(i=0;i<n;height[rank[i++]]=k)
47         for(k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++);
48 }
49 
50 inline void go()
51 {
52     int n,ans=0;
53     scanf("%s",str);
54     n=strlen(str);
55     for(int i=0;i<n;i++) r[i]=str[i];
56     r[n]=0;
57     da(r,sa,n+1,256);
58     getheight(r,sa,n);
59     for(int i=1;i<=n;i++) ans+=n-sa[i]-height[i];
60     printf("%d\n",ans);
61 }
62 
63 int main()
64 {
65     int cas;scanf("%d",&cas);
66     while(cas--) go();
67     return 0;
68 }

 

 

posted @ 2013-02-05 21:04  proverbs  阅读(218)  评论(0编辑  收藏  举报