后缀数组题目

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 }

 

你可能感兴趣的:(后缀数组)