码迷,mamicode.com
首页 > 其他好文 > 详细

HDU 5008 求第k小子串

时间:2015-11-25 00:24:23      阅读:290      评论:0      收藏:0      [点我收藏+]

标签:

本题要求第k小的distinct子串,可以根据height数组,二分出这个第k小子串所在后缀的位置信息。由于题目要求子串起始下标尽可能小。所以再在rank数组中,二分出与当前后缀LCP大于等于所求子串长度的范围。通过RMQ求出这个范围中最小的sa。

技术分享
  1 #include <iostream>
  2 #include <vector>
  3 #include <algorithm>
  4 #include <string>
  5 #include <string.h>
  6 #include <stdio.h>
  7 #include <queue>
  8 #include <stack>
  9 #include <map>
 10 #include <set>
 11 #include <cmath>
 12 #include <ctime>
 13 #include <cassert>
 14 #include <sstream>
 15 using namespace std;
 16 
 17 const int N=123456;
 18 
 19 int MIN(int a,int b){return a<b?a:b;}
 20 int MAX(int a,int b){return a>b?a:b;}
 21 
 22 int val[N];
 23 struct RMQ {
 24     int dp[N][22];
 25     int (*cmp) (int,int);
 26     void setMin(){cmp=MIN;}
 27     void setMax(){cmp=MAX;}
 28     void init(int n,int *val) {
 29         for (int i=1; i<=n; i++)
 30             dp[i][0]=val[i];
 31         for (int j=1; (1<<j)<=n; j++) {
 32             int k=1<<(j-1);
 33             for (int i=1; i+k<=n; i++)
 34                 dp[i][j]=cmp(dp[i][j-1],dp[i+k][j-1]);
 35         }
 36     }
 37     int query(int a,int b) {
 38         if (a>b) swap(a,b);
 39         int dis=b-a+1;
 40         int k=log((double)dis)/log(2.0);
 41         return cmp(dp[a][k],dp[b-(1<<k)+1][k]);
 42     }
 43 }rmq;
 44 char s[N];
 45 struct SuffixArray {;
 46     int sa[N];
 47     int t1[N],t2[N],c[N];
 48     int rk[N],height[N];
 49     long long sum[N];
 50     inline int cmp(int *r,int a,int b,int l){
 51         return r[a]==r[b]&&r[a+l]==r[b+l];
 52     }
 53     void calcSA (char *s,int n,int m) {
 54         int i,j,p,*x=t1,*y=t2;
 55         for(i=0;i<m;i++)c[i]=0;
 56         for(i=0;i<n;i++)c[x[i]=s[i]]++;
 57         for(i=1;i<m;i++)c[i]+=c[i-1];
 58         for(i=n-1;i>=0;i--)sa[--c[x[i]]]=i;
 59         for(j=1;j<=n;j<<=1){
 60             p=0;
 61             for(i=n-j;i<n;i++)y[p++]=i;
 62             for(i=0;i<n;i++)if(sa[i]>=j)y[p++]=sa[i]-j; // 排名从小到大,如果pos比j大,则suffix(sa[i]-j)的第二关键字为p
 63             for(i=0;i<m;i++)c[i]=0;
 64             for(i=0;i<n;i++)c[x[y[i]]]++;
 65             for(i=1;i<m;i++)c[i]+=c[i-1];
 66             for(i=n-1;i>=0;i--)sa[--c[x[y[i]]]]=y[i]; // 根据第二关键字从大到小,确定新一轮sa
 67             swap(x,y);
 68             p=1;x[sa[0]]=0;
 69             for(i=1;i<n;i++)
 70                 x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
 71             if(p>=n)break;
 72             m=p;
 73         }
 74     }
 75     void calcHeight(char *s,int n) {
 76         int i,j,k=0;
 77         for(i=0;i<=n;i++)rk[sa[i]]=i;
 78         for(i=0;i<n;i++){
 79             if(k)k--; // h[i]>=h[i-1]-1
 80             j=sa[rk[i]-1]; // suffix(j)排名在suffix(i)前一位
 81             while(s[i+k]==s[j+k])k++; // 暴力计算lcp
 82             height[rk[i]]=k;
 83         }
 84         sum[0]=0;
 85         for (int i=1;i<=n;i++) sum[i]=sum[i-1]+n-sa[i]-height[i];
 86     }
 87     int lcp(int a,int b,int len) {
 88         if (a==b) return len-a;
 89         int ra=rk[a],rb=rk[b];
 90         if (ra>rb) swap(ra,rb);
 91         return queryST(ra+1,rb);
 92     }
 93     int st[N][25];
 94     void initST(int n) {
 95         for (int i=1;i<=n;i++)
 96             st[i][0]=height[i];
 97         for (int j=1;(1<<j)<=n;j++) {
 98             int k=1<<(j-1);
 99             for (int i=1; i+k<=n; i++)
100                 st[i][j]=min(st[i][j-1],st[i+k][j-1]);
101         }
102     }
103     int queryST(int a,int b) {
104         if (a>b) swap(a,b);
105         int dis=b-a+1;
106         int k=log((double)dis)/log(2.0);
107         return min(st[a][k],st[b-(1<<k)+1][k]);
108     }
109     void solve(int &l,int &r,long long k,int n) {
110         if (k<1||k>sum[n]) {
111             l=0;r=0;
112             return;
113         }
114         int t=lower_bound(sum,sum+n+1,k)-sum;
115         assert(t>=1&&t<=n);
116         long long now=sum[t-1];
117         int need=k-now;
118         l=sa[t],r=sa[t]+height[t]+need-1;
119         int len=r-l+1;
120         int le=t,ri=n,ret=l;
121         while (le<=ri) {
122             int mid=(le+ri)/2;
123             if (lcp(sa[mid],l,n)>=len) {
124                 le=mid+1;
125                 ret=mid;
126             } else ri=mid-1;
127         }
128         l=rmq.query(t,ret);
129         l++;
130         r=l+len-1;
131     }
132 }suf;
133 
134 int main () {
135     while (scanf("%s",s)!=EOF) {
136         int n=strlen(s);
137         suf.calcSA(s,n+1,128);
138         suf.calcHeight(s,n);
139         suf.initST(n);
140         rmq.setMin();
141         rmq.init(n,suf.sa);
142         int Q;
143         scanf("%d",&Q);
144         int l=0,r=0; //int cnt=0;
145         while (Q--) {
146             long long k;
147             scanf("%I64d",&k);
148             k^=(l^r);
149             k++;
150             //k=++cnt;
151             suf.solve(l,r,k,n);
152             printf("%d %d\n",l,r);
153         }
154     }
155     return 0;
156 }
View Code

 

HDU 5008 求第k小子串

标签:

原文地址:http://www.cnblogs.com/micrari/p/4993382.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!