首页 > 代码库 > hdu 5008

hdu 5008

因为一看到有关子串就主动的联想后缀数组

 

所有后缀的前缀去重后就是所有子串(好像是废话)

这样就可以得到每个后缀的子串个数。

二分查找到第k个所在的位置。

在二分处理所有可以出现该串的sa区间。

最小就是维护sa数组。

 

  1 //rank从0开始  2 //sa从1开始,因为最后一个字符(最小的)排在第0位  3 //high从2开始,因为表示的是sa[i-1]和sa[i]  4 #include <cstdio>  5 #include <cstring>  6 #include <iostream>  7 #include <algorithm>  8 #define root 1,n,1  9 #define lson l,mid,rt<<1 10 #define rson mid+1,r,rt<<1|1 11 #define M 120010 12 #define INF 10000007 13 using namespace std; 14 typedef long long LL; 15 LL rank[M],sa[M],X[M],Y[M],high[M],init[M]; 16 LL buc[M]; 17 struct SegmentTree{ 18     LL Min[M << 2]; 19     void pushup(LL rt){ 20         Min[rt] = min(Min[rt<<1],Min[rt<<1|1]); 21     } 22     void build(LL l,LL r,LL rt){ 23         if(l == r){ 24             Min[rt] = sa[l]; 25             return; 26         } 27         LL mid = (l + r) >> 1; 28         build(lson); 29         build(rson); 30         pushup(rt); 31     } 32     LL query(LL L,LL R,LL l,LL r,LL rt){ 33         if(L <= l && r <= R){ 34             return Min[rt]; 35         } 36         LL mid = (l + r) >> 1; 37         LL ans = INF; 38         if(L <= mid) ans = min(query(L,R,lson),ans); 39         if(mid + 1 <= R) ans = min(query(L,R,rson),ans); 40         return ans; 41     } 42 }; 43 SegmentTree sgt; 44 void calhigh(LL n) { 45     LL i , j , k = 0; 46     for(i = 1 ; i <= n ; i++) rank[sa[i]] = i; 47     for(i = 0 ; i < n ; high[rank[i++]] = k) 48         for(k?k--:0 , j = sa[rank[i]-1] ; init[i+k] == init[j+k] ; k++); 49 } 50 bool cmp(LL *r,LL a,LL b,LL l) { 51     return (r[a] == r[b] && r[a+l] == r[b+l]); 52 } 53 void suffix(LL n,LL m = 128) { 54     LL i , l , p , *x = X , *y = Y; 55     for(i = 0 ; i < m ; i ++) buc[i] = 0; 56     for(i = 0 ; i < n ; i ++) buc[ x[i] = init[i]  ] ++; 57     for(i = 1 ; i < m ; i ++) buc[i] += buc[i-1]; 58     for(i = n - 1; i >= 0 ; i --) sa[ --buc[ x[i] ]] = i; 59     for(l = 1,p = 1 ; p < n ; m = p , l *= 2) { 60         p = 0; 61         for(i = n-l ; i < n ; i ++) y[p++] = i; 62         for(i = 0 ; i < n ; i ++) if(sa[i] >= l) y[p++] = sa[i] - l; 63         for(i = 0 ; i < m ; i ++) buc[i] = 0; 64         for(i = 0 ; i < n ; i ++) buc[ x[y[i]] ] ++; 65         for(i = 1 ; i < m ; i ++) buc[i] += buc[i-1]; 66         for(i = n - 1; i >= 0 ; i --) sa[ --buc[ x[y[i]] ] ] = y[i]; 67         for(swap(x,y) , x[sa[0]] = 0 , i = 1 , p = 1 ; i < n ; i ++) 68             x[ sa[i] ] = cmp(y,sa[i-1],sa[i],l) ? p-1 : p++; 69     } 70     calhigh(n-1);//后缀数组关键是求出high,所以求sa的时候顺便把rank和high求出来 71 } 72  73  74 //当需要反复询问两个后缀的最长公共前缀时用到RMQ 75 LL Log[M]; 76 LL best[20][M]; 77 void initRMQ(LL n) {//初始化RMQ 78     for(LL i = 1; i <= n ; i ++) best[0][i] = high[i]; 79     for(LL i = 1; i <= Log[n] ; i ++) { 80         LL limit = n - (1<<i) + 1; 81         for(LL j = 1; j <= limit ; j ++) { 82             best[i][j] = min(best[i-1][j] , best[i-1][j+(1<<i>>1)]); 83         } 84     } 85 } 86 LL lcp(LL a,LL b) {//询问a,b后缀的最长公共前缀 87     //a = rank[a];    b = rank[b]; 88     //if(a > b) swap(a,b); 89     //a ++; 90     if(a>b)return 0; 91     LL t = Log[b - a + 1]; 92     return min(best[t][a] , best[t][b - (1<<t) + 1]); 93 } 94  95 char str[M]; 96 LL dlen[M]; 97  98 void getlen(LL n,LL len){ 99     //dlen[1]=len-sa[1];100     for(LL i=1;i<=n;i++){101         dlen[i]=len-sa[i]-high[i];102         //printf("%d ",dlen[i]);103     }104     //cout<<endl;105     for(LL i=1;i<=n;i++){106         dlen[i]+=dlen[i-1];107     }108 }109 void findK(LL n,LL k,LL &start,LL &len){110     //printf("%I64d %I64d\n",k,dlen[n]);111     if(k>dlen[n]||k<=0){112         start=len=0;113         return ;114     }115     LL l=1,r=n;116     while(l<=r){117         LL mid=(l+r)>>1;118         if(dlen[mid]>=k) r=mid-1;119         else l=mid+1;120     }121     start=l;122     len=k-dlen[start-1]+high[start];123     //printf("start:%I64d len:%I64d\n",start,len);124 }125 LL findLcp(LL start,LL len,LL n){126     LL l=start + 1,r=n;127     while(l<=r){128         LL mid=(l+r)>>1;129         if(lcp(start+1,mid)>=len)l=mid+1;130         else r=mid-1;131     }132     //printf("start:%I64d r:%I64d\n",start,r);133     return sgt.query(start,r,root);134 }135 int main() {136     //预处理每个数字的Log值,常数优化,用于RMQ137     LL l,r,q;138     LL v,u;139     LL start,llen;140     Log[0] = -1;141     for(LL i = 1; i < M ; i ++) {142         Log[i] = (i&(i-1)) ? Log[i-1] : Log[i-1] + 1 ;143     }144     //*******************************************145     //    n为数组长度,下标0开始146     //    将初始数据,保存在init里,并且保证每个数字都比0大147     //    m = max{ init[i] } + 1148     //    一般情况下大多是字符操作,所以128足够了149     //*******************************************150 151     while(scanf("%s",str)!=EOF){152         l = r  = 0;153         LL n=strlen(str);154         for(LL i=0;i<n;i++)init[i]=str[i];155         init[n]=0;156         suffix(n+1);157         initRMQ(n);158         getlen(n,n);159         sgt.build(root);160         scanf("%I64d",&q);161         for(LL i=1;i<=q;i++){162             scanf("%I64d",&v);163             u=(l^r^v)+1;164             findK(n,u,start,llen);165             //printf("u:%I64d\n",u);166             if(llen==0&&start==0){167                 l=0,r=0;168                 printf("0 0\n");169                 continue;170             }171             LL ans=findLcp(start,llen,n);172             l=ans;r=ans+llen-1;173             l++;r++;174             printf("%I64d %I64d\n",l,r);175         }176     }177 //    init[n] = 0;178 //    suffix(n+1,m);179 }180 /*181 acab182 10183 2184 */
View Code

 

hdu 5008