后缀数组题目

hdu 3948

  1 /*
  2 题意:给你一个串,求该串的不同子回文串的个数;
  3 
  4 分析:首先我们想一下一个比较简单的问,求一个串的不同子串的个数
  5 显然每个子串都是一个后缀的前缀,那么只要按照SA[]统计该后缀i的前缀
  6 有那些是出现过的,也就是ans+=len[该后缀长]-height[i];
  7 
  8 
  9 同样的道理,我们先统计出以i为中心的回文串到最右边的距离,然后
 10 再统计不同回文串的个数,即减去相同回文串的个数,但是两者还是有区别的
 11 其次,为了统一奇偶的差别,我们构造出另一个字符串,
 12 s1=aabaa  -> s=$#a#a#b#a#a#  (可以参看本博客前前篇文章MANACHER) 
 13 首先我用MANACHER的预处理出以s[i]为中心的回文串到最右边的距离记录在lc[i];
 14 当然可以直接用后缀数组的方法求出(将反串加在原串后面,然后询问后缀i和后缀2*n-i的LCP,但要分奇偶)
 15 
 16 统计不同子串个数和统计不同回文串的个数的区别
 17 看样例:aabaa
 18 
 19 
 20 构造的串:$#a#a#b#a#a#
 21 后缀  lc[i]        回文串的个数     
 22 11    1    #            1
 23 9     3    #a#           3        
 24 7     1    #a#a#        1     
 25 1     1    #a#a#b#a#a#    1        
 26 3     3    #a#b#a#a#    3    & 
 27 5     1    #b#a#a#        1
 28                     
 29 0     1    $#a#a#b#a#a#    1
 30 10    2    a#            2
 31 8     2    a#a#        2
 32 2     2    a#a#b#a#a#    2
 33 4     2    a#b#a#a#        2
 34 6     6    b#a#a#        6
 35 
 36 先不考虑加入#后产生的回文串,
 37 在统计后缀3中包含的回文串时,会把#a,和#a#统计进去,但我们发现在统计后缀9时#a,#a#就已经被统计了
 38 也就是说统计后缀i中已经被统计过的回文串的个数,不一定只是后缀i-1中的回文,还有可能是更前面的,
 39 所以我们在遍历的过程中设置一个标记tmp,表示以字符i为中点能延伸的最长距离,而不是该串在原串中能延伸的距离
 40 
 41 至于加入#后多出了的回文串,可以发现不管是lc[]还是height[]长度都是延伸到#位置的也就是相减后/2就是原串的个数
 42 还有加进了#最后答案要减去1; 
 43  
 44 */
 45 #include<cstdio>
 46 #include<cstring>
 47 #include<cstdlib>
 48 #include<iostream>
 49 #include<algorithm>
 50 #include<cmath>
 51 #include<vector>
 52 using namespace std;
 53 typedef long long LL;
 54 const int N=200000+10;
 55 char s[N],s1[N];
 56 struct SuffixArray{
 57     int a1[N],a2[N],c[N],SA[N],sa[N],*rank,*x,*y;
 58     int n,height[N],m;
 59     void sort(){
 60         for (int i=0;i<m;i++) c[i]=0;
 61         for (int i=0;i<n;i++) c[x[i]]++;
 62         for (int i=0;i<m;i++) c[i+1]+=c[i];
 63         for (int i=n-1;i>=0;i--) SA[ --c[x[sa[i]]] ] = sa[i];
 64     }
 65     void build_sa(char *s){
 66         n=strlen(s); m=256;
 67         x=a1; y=a2; x[n]=y[n]=-1;
 68         for (int i=0;i<n;i++) x[i]=s[i],sa[i]=i;
 69         sort();
 70         for (int k=1;k<=n;k<<=1){
 71             int p=0;
 72             for (int i=n-k;i<n;i++) sa[p++]=i;
 73             for (int i=0;i<n;i++) if (SA[i]>=k) sa[p++]=SA[i]-k;
 74             sort();
 75             p=0; swap(x,y);
 76             x[SA[0]]=0;
 77             for (int i=1;i<n;i++){
 78                 if ( y[ SA[i-1] ]!=y[ SA[i] ] || y[ SA[i-1]+k ]!=y[ SA[i]+k ] ) p++;
 79                 x[SA[i]]=p;
 80             }
 81             if (p+1==n) break;
 82             m=p+1;
 83         }
 84         rank=x; getHeight(s);
 85     }
 86     void getHeight(char *s){
 87         int k=0;
 88         for (int i=0;i<n;i++){
 89             if (k) k--;
 90             if (rank[i]==0) continue;
 91             int j=SA[ rank[i]-1 ];
 92             while (i+k<n && j+k<n && s[i+k]==s[j+k]) k++;
 93             height[rank[i]]=k;
 94         }
 95         height[n]=0;
 96     }
 97 }H;
 98 int lc[N*2];
 99 int lx[N],ly[N];
100 void init(char *s1){
101     int c=0;
102     int n=strlen(s1);
103     s[c++]='$';s[c++]='#';
104     for (int i=0;s1[i];i++){
105         s[c++]=s1[i];
106         s[c++]='#';
107     }s[c]='\0';
108     lc[0]=1;lc[1]=1;
109     int k=1;
110     for (int i=2;i<c;i++){
111         int p=k+lc[k]-1;
112         if (i>=p){
113             int j=0;
114             while (i-j>=0 && i+j<c && s[i-j]==s[i+j]) j++;
115             lc[i]=j; k=i;
116         }else {
117             int j=2*k-i;
118             if (i+lc[j]-1<p) lc[i]=lc[j];
119             else {
120                 int d=p-i+1;
121                 while (i-d>=0 && i+d<c && s[i-d]==s[i+d]) d++;
122                 lc[i]=d; k=i;
123             } 
124         }
125     }
126 
127 }
128 void solve(){
129     int n=strlen(s);
130     int ret=lc[H.SA[0]];
131     int tmp=lc[H.SA[0]];
132 //    cout<<s<<endl;
133 
134 //    cout<<H.SA[0]<<"    "<<lc[H.SA[0]]<<"    "<<s+H.SA[0]<<endl;
135     for (int i=1;i<n;i++){
136         int t=H.SA[i];
137 //        cout<<H.SA[i]<<"    "<<lc[H.SA[i]]<<"    "<<s+H.SA[i]<<endl;
138         ret+=max(0,(lc[t]-min(H.height[i],tmp))/2);//tmp表示以字符s[H.SA[i-1]]为中心已经被统计过的回文串的个数 
139         if (lc[t]>=tmp) tmp=lc[t];
140         else {
141             tmp=min(tmp,H.height[i]);//从上一个传递到当前; 
142             if (lc[t]>tmp) tmp=lc[t];//如果该后缀本身的回文个数更多久更新; 
143         }
144     }
145     printf("%d\n",ret-1);
146 }
147 int main(){
148     int T,cas=0;
149     scanf("%d",&T);
150     while (T--){
151         scanf("%s",s1);
152         init(s1);
153         H.build_sa(s);
154         printf("Case #%d: ",++cas);
155         solve();
156     }
157     return 0;
158 }
159 /*
160 6 
161 abba
162 baab
163 aabaa
164 aaaa
165 abab
166 abcd
167 
168 
169 */

 

hdu 3518

  1 /*
  2 题意:统计一个字符串里不错至少2次且不覆盖的子串的个数
  3 
  4 后缀数组求出height后,枚举子串的长度L,利用L对height分组
  5 对于同一组里的后缀,如果在组里最小后缀和最大后缀的差大于L就说明至少有俩个长度为L串
  6 且不覆盖的出现,所以ans++;
  7 
  8 为什么这样不会重复统计,假设长度为L的字串s1是满足要求的串 
  9 让我们看一下长度为L的子串s1是如何被统计进去的,
 10 首先我们知道所有前缀是s1的后缀在SA[]数组里将连续出现,而且相邻的height[]>=L;
 11 也就是说只要其中俩个后缀的距离大于等于L就说明他们不重叠;
 12 所以串s1只会被统计一次;
 13 时间复杂度是o(n^2); 
 14  
 15 */
 16 #include<cstdio>
 17 #include<cstdlib>
 18 #include<cstring>
 19 #include<iostream>
 20 #include<algorithm>
 21 #include<vector>
 22 using namespace std;
 23 const int N=1000+10;
 24 struct SuffixArray{
 25     int a1[N],a2[N],c[N],SA[N],sa[N],*rank,*x,*y;
 26     int n,height[N],m;
 27     void sort(){
 28         for (int i=0;i<m;i++) c[i]=0;
 29         for (int i=0;i<n;i++) c[x[i]]++;
 30         for (int i=0;i<m;i++) c[i+1]+=c[i];
 31         for (int i=n-1;i>=0;i--) SA[ --c[x[sa[i]]] ] = sa[i];
 32     }
 33     void build_sa(char *s){
 34         n=strlen(s); m=256;
 35         x=a1;y=a2; x[n]=y[n]=-1;
 36         for (int i=0;i<n;i++) x[i]=s[i],sa[i]=i;
 37         sort();
 38         for (int k=1;k<=n;k<<=1){
 39             int p=0;
 40             for (int i=n-k;i<n;i++) sa[p++]=i;
 41             for (int i=0;i<n;i++) if (SA[i]>=k) sa[p++]=SA[i]-k;
 42             sort();
 43             p=0; swap(x,y);
 44             x[SA[0]]=0;
 45             for (int i=1;i<n;i++){
 46                 if (y[ SA[i-1] ]!=y[ SA[i] ] || y[ SA[i-1]+k ]!=y[ SA[i]+k ] ) p++;
 47                 x[SA[i]]=p;
 48             }
 49             if (p+1==n) break;
 50             m=p+1;
 51         }
 52         rank=x;getHeight(s);
 53     }
 54     void getHeight(char *s){
 55         int k=0;
 56         for (int i=0;i<n;i++){
 57             if (k) k--;
 58             if (rank[i]==0) continue;
 59             int j=SA[ rank[i]-1 ];
 60             while (i+k<n && j+k<n && s[i+k]==s[j+k]) k++;
 61             height[ rank[i] ]=k;
 62         }
 63         height[n]=0;
 64     }
 65 }H;
 66 char s[N];
 67 vector<int > g;
 68 void solve(){
 69     int ret=0;
 70     int n=strlen(s);
 71 /*    cout<<">.<"<<endl;
 72     for (int i=0;i<n;i++) cout<<H.SA[i]<<" ";cout<<endl;
 73     for (int i=0;i<n;i++) cout<<H.rank[i]<<" ";cout<<endl;
 74     for (int i=1;i<n;i++)cout<<H.height[i]<<" ";cout<<endl;
 75     cout<<">.<"<<endl;
 76 */    for (int i=n/2;i>=1;i--){
 77         g.clear();
 78         for (int j=1;j<=n;j++){
 79             if (H.height[j]>=i){
 80                 g.push_back(H.SA[j-1]);
 81             }else {
 82                 g.push_back(H.SA[j-1]);
 83                 sort(g.begin(),g.end());
 84                 int sz=g.size();
 85                 if (sz>1 && g[sz-1]-g[0]>=i) ret++;
 86                 g.clear();
 87             }
 88         }
 89         
 90     }
 91     printf("%d\n",ret);
 92 }
 93 int main(){
 94     while (~scanf("%s",&s)){
 95         if (s[0]=='#') break;
 96         H.build_sa(s);
 97         solve();
 98     }
 99     return 0;
100 }
101 /*
102 aaaa
103 ababcabb
104 aaaaaa
105 #
106 
107 */

 

hdu 4416

  1 /*
  2 题意:求出现在s串中,但不出现在其他n个串中的字串个数;
  3 
  4 类似统计求一个串中不同的子串个数;
  5  
  6 我的做法是,先链接这些串,定义起点在s中的后缀叫目标后缀;
  7 求从前往后,从后往前扫两边,求出每一个目标后缀在SA[]数组中前后
  8 最近的非目标后缀和其的LCP,Lx[]中保存的是两个中的较大值;
  9 因为有可能多个目标后缀在SA[]数组中是连续的,所以还要减去其中一部分
 10 比如
 11 2 bab 
 12 0 babab
 13 1 babac
 14 3 babd
 15 0,1都是目标后缀,2,3都是非目标后缀,那么0,1后缀的非法子串是bab
 16 有因为0,1后缀相邻,要减去(height[]-lx[]); 
 17 所以在统计的时候baba才只被统计一次,
 18  
 19 那么剩下的就是要求的串的个数, 
 20 */
 21 #include<cstdio>
 22 #include<cstring>
 23 #include<iostream>
 24 #include<algorithm>
 25 #include<cmath>
 26 #include<cstdlib>
 27 #include<vector>
 28 using namespace std;
 29 typedef long long LL;
 30 const int N=300000+10;
 31 struct SuffixArray{
 32     int a1[N],a2[N],c[N],SA[N],sa[N],*rank,*x,*y;
 33     int n,height[N],m;
 34     void sort(){
 35         for (int i=0;i<m;i++) c[i]=0;
 36         for (int i=0;i<n;i++) c[x[i]]++;
 37         for (int i=0;i<m;i++) c[i+1]+=c[i];
 38         for (int i=n-1;i>=0;i--) SA[ --c[x[sa[i]]] ] = sa[i];
 39     }
 40     void build_sa(char *s){
 41         n=strlen(s); m=256;
 42         x=a1; y=a2; x[n]=y[n]=-1;
 43         for (int i=0;i<n;i++) x[i]=s[i],sa[i]=i;
 44         sort();
 45         for (int k=1;k<=n;k<<=1){
 46             int p=0;
 47             for (int i=n-k;i<n;i++) sa[p++]=i;
 48             for (int i=0;i<n;i++) if (SA[i]>=k) sa[p++]=SA[i]-k;
 49             sort();
 50             p=0; swap(x,y);
 51             x[SA[0]]=0;
 52             for (int i=1;i<n;i++){
 53                 if ( y[ SA[i-1] ]!=y[ SA[i] ] || y[ SA[i-1]+k ]!=y[ SA[i]+k ] ) p++;
 54                 x[SA[i]]=p;
 55             }
 56             if (p+1==n) break;
 57             m=p+1;
 58         }
 59         rank=x; getHeight(s);
 60     }
 61     void getHeight(char *s){
 62         int k=0;
 63         for (int i=0;i<n;i++){
 64             if (k) k--;
 65             if (rank[i]==0) continue;
 66             int j=SA[ rank[i]-1 ];
 67             while (i+k<n && j+k<n && s[i+k]==s[j+k]) k++;
 68             height[rank[i]]=k;
 69         }
 70         height[n]=0;
 71     }
 72 }H;
 73 char s[N],tmps[N];
 74 int L;
 75 void read(){
 76     int n;scanf("%d",&n);
 77     scanf("%s",s);
 78     L=strlen(s);
 79     int le=strlen(s); s[le]='$';s[++le]='\0'; 
 80     for (int i=0;i<n;i++){
 81         scanf("%s",tmps);
 82         int l=strlen(tmps);
 83         strcpy(s+le,tmps);
 84         le+=l;
 85         s[le]='#'; s[++le]='\0';
 86     }s[le]='{';s[le+1]='\0';
 87     //cout<<s<<endl;
 88 }
 89 int lx[N];
 90 
 91 void solve(){
 92     int n=strlen(s);
 93     int tmp=H.height[1];
 94     for (int i=0;i<L;i++) lx[i]=0;
 95     for (int i=1;i<n-1;i++){
 96         if (H.SA[i]<L){
 97             lx[H.SA[i]]=tmp;
 98             tmp=min(tmp,H.height[i+1]);
 99         }else {
100             tmp=H.height[i+1];
101         }    
102     }
103     tmp=H.height[n-1];
104     for (int i=n-2;i>0;i--){
105         if (H.SA[i]<L){
106             if (lx[H.SA[i]]<tmp) lx[H.SA[i]]=tmp;
107             tmp=min(tmp,H.height[i]);
108         }else {
109             tmp=H.height[i];
110         }
111     }
112     LL ret=0;
113     
114     for (int i=1;i<n;i++){
115         if (H.SA[i]<L){
116             ret+=L-H.SA[i]-lx[H.SA[i]];
117             if (H.SA[i-1]<L){
118                 if (H.height[i]>lx[H.SA[i]]) ret-=H.height[i]-lx[H.SA[i]];
119             }
120         }
121     }
122     printf("%I64d\n",ret);
123 }
124 int main(){
125     int T,cas=0;scanf("%d",&T);
126     while (T--){
127         read();
128         H.build_sa(s);
129         printf("Case %d: ",++cas);
130         solve();
131     }
132     return 0;
133 }

 

posted @ 2013-07-03 22:32  Rabbit_hair  阅读(1639)  评论(0编辑  收藏  举报