K-th occurrence(后缀树组+划分树+ST表+RMQ+二分)
Posted kkkek
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了K-th occurrence(后缀树组+划分树+ST表+RMQ+二分)相关的知识,希望对你有一定的参考价值。
题目大意:
T个测试样例。一个长度为N的字符串S,之后Q个[l,r,k],表示一个子串S[l,r],求出第k个该子串的下标。起始坐标为1。不存在输出-1。
数据范围:1≤T≤20, 1≤N≤105, 1≤Q≤105, 1≤l≤r≤N, 1≤k≤N, |S|=N;
赛后补题。参考题解说后缀树组+划分树+ST表+二分。
比赛的时候只会后缀树组不会划分树,赛后仔细想,觉得后缀数组可以,然而并不,会TLE。
补提的时候先是采用后缀树组+划分树+RMQ+二分,还是TLE了。
之后改成后缀树组+划分树+ST表+二分,RMQ是随手用。
1 //kkkek 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 using namespace std; 7 typedef long long ll; 8 const int mod=998244353; 9 const int maxn=1e5+50; 10 11 /*主代码:后缀数组+ST表+划分树+二分check*/ 12 13 /***************后缀数组**************/ 14 int wa[maxn],wb[maxn],wv[maxn]; 15 int cmp(int *r,int a,int b,int k) 16 17 return r[a]==r[b]&&r[a+k]==r[b+k]; 18 19 void da(int *r,int *sa,int n,int m,int *ws) 20 2019-08-282019-08-282019-08-282019-08-282019-08-28 21 int i,j,p,*x=wa,*y=wb,*t; 22 for(i=0;i<m;i++)ws[i]=0; 23 for(i=0;i<n;i++)ws[x[i]=r[i]]++; 24 for(i=1;i<m;i++)ws[i]+=ws[i-1]; 25 for(i=n-1;i>=0;i--)sa[--ws[x[i]]]=i; 26 for(j=1,p=1;p<n;j*=2,m=p) 27 28 for(p=0,i=n-j;i<n;i++)y[p++]=i; 29 for(i=0;i<n;i++)if(sa[i]>=j)y[p++]=sa[i]-j; 30 for(i=0;i<n;i++)wv[i]=x[y[i]]; 31 for(i=0;i<m;i++)ws[i]=0; 32 for(i=0;i<n;i++)ws[wv[i]]++; 33 for(i=1;i<m;i++)ws[i]+=ws[i-1]; 34 for(i=n-1;i>=0;i--)sa[--ws[wv[i]]]=y[i]; 35 for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1;i<n;i++) 36 x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++; 37 38 return; 39 40 int height[maxn]; 41 void calheight(int *r,int *sa,int n,int *rank) 42 43 memset(height,0,sizeof(height)); 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 return; 49 //sa[排名]=下标 rank[下标]=排名 50 //height[排名i]=排名为i的数组与排名为i-1数组的最长前缀的长度 51 52 /******************划分树******************/ 53 int sorted[maxn]; 54 int num[20][maxn],val[20][maxn]; 55 void build(int l,int r,int ceng) 56 57 if(l==r)return; 58 int mid=(l+r)>>1,isame=mid-l+1,i; 59 for(i=l;i<=r;i++)if(val[ceng][i]<sorted[mid])isame--; 60 int ln=l,rn=mid+1; 61 for(i=l;i<=r;i++) 62 63 if(i==l)num[ceng][i]=0; 64 else num[ceng][i]=num[ceng][i-1]; 65 if(val[ceng][i]<sorted[mid]||val[ceng][i]==sorted[mid]&&isame>0) 66 67 val[ceng+1][ln++]=val[ceng][i]; 68 num[ceng][i]++; 69 if(val[ceng][i]==sorted[mid])isame--; 70 71 else val[ceng+1][rn++]=val[ceng][i]; 72 73 build(l,mid,ceng+1);build(mid+1,r,ceng+1); 74 75 int look(int ceng,int sl,int sr,int l,int r,int k) 76 77 if(sl==sr)return val[ceng][sl]; 78 int ly; 79 if(l==sl)ly=0; 80 else ly=num[ceng][l-1]; 81 int tolef=num[ceng][r]-ly; 82 if(tolef>=k) 83 84 return look(ceng+1,sl,(sl+sr)/2,sl+ly,sl+num[ceng][r]-1,k); 85 86 else 87 88 int lr=(sl+sr)/2+1+(l-sl-ly); 89 return look(ceng+1,(sl+sr)/2+1,sr,lr,lr+r-l+1-tolef-1,k-tolef); 90 91 //(0,1,n,l,r,k)找数组l与r之间第k大的数的数值,返回该数值 92 93 /***********************ST表******************/ 94 int st[maxn][25]; 95 void initST(int n,int *a) 96 97 memset(st,0,sizeof(st)); 98 for(int i=0;i<=n;i++)st[i][0]=a[i]; 99 for(int j=1;(1<<j)<=n;j++) 100 for(int i=0;i+(1<<j)-1<=n;i++) 101 st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1]); 102 103 int askST(int l,int r) 104 105 if(l>r)swap(l,r); 106 int k=log2(r-l+1);//(int)(log((double)(r-l+1))/log(2.0)); 107 return min(st[l][k],st[r-(1<<k)+1][k]); 108 //askST(l,r)找数组中l,r之间的最小值,返回该最小值 109 int askRMQ(int ra,int rb) 110 111 if(ra>rb)swap(ra,rb); 112 int k=0; 113 while(1<<(k+1)<=rb-ra)k++; 114 return min(st[ra+1][k],st[rb-(1<<k)+1][k]);//len 115 //askRMQ(rank[a],rank[b])找下标a,b(或者排名ra,rb)的后缀数组的最长公共前缀 116 //前驱套用st表前驱(长得一毛一样当然就不再init一遍啦) 117 118 /******************二分查找*********************************/ 119 //用ST表判断长度是否合理 不合理二分缩小查找范围 120 int check(int l,int r,int ju,int len) 121 122 if(l==r)return l; 123 int mid=(l+r)>>1; 124 if(!ju) 125 126 if(l>r)return l; 127 if(askST(l,r)>=len)return r; 128 if(askST(l,mid)>=len)return check(mid,r-1,0,len); 129 else 130 131 if(l+1==mid)return l; 132 return check(l,mid-1,0,len); 133 134 135 else 136 137 if(r<l)return r; 138 if(askST(l,r)>=len)return l; 139 if(askST(mid,r)>=len)return check(l+1,mid,1,len); 140 else 141 142 if(mid+1==r)return r; 143 return check(mid+1,r,1,len); 144 145 146 //找到给定子串l的rank[l]排位前后的子串的最小下标与最大小标,即,上下界 147 148 int main() 149 150 int T; 151 scanf("%d",&T); 152 while(T--) 153 154 int n,q,i,j,r[maxn]=0,sa[maxn]=0,ws[maxn]=0,rank[maxn]=0,L,R,k,len,ra; 155 char s[maxn]="\0"; 156 scanf("%d%d",&n,&q); 157 scanf("%s",s); 158 for(i=0;i<n;i++)r[i]=s[i]-‘a‘+1; 159 r[n]=0;n++; 160 da(r,sa,n,30,ws);//后缀数组求出sa[]和rank[] 161 calheight(r,sa,n-1,rank);//得height[]数组 为之后查找上下界做准备 162 163 for(i=0;i<n;i++)sorted[i]=sa[i],val[0][i]=sa[i]; 164 sort(sorted+1,sorted+n);//为划分树准备 165 build(1,n-1,0);//划分树预处理 166 167 initST(n-1,height);//ST表预处理 168 169 while(q--) 170 171 scanf("%d%d%d",&L,&R,&k); 172 L--;R--;//下标与题意下标对应 173 174 ra=rank[L]; 175 len=R-L+1; 176 177 //二分查找 178 if(height[ra]<len&&height[ra+1]!=0&&askRMQ(ra,ra+1)>=len) 179 180 L=ra; 181 R=check(ra+1,n-1,0,len);//这里!一定要ra+1 因为height[].... 为了这个debug好久=皿= 182 183 else if(height[ra]<len) 184 185 L=R=ra; 186 187 else 188 189 if(height[ra+1]==0)R=ra; 190 else R=check(ra,n-1,0,len); 191 L=check(1,ra,1,len); 192 193 if(askRMQ(ra,L-1)>=len&&L!=1)L--; 194 if(R-L+1<k)printf("-1\n"); 195 else printf("%d\n",look(0,1,n-1,L,R,k)+1); 196 197 198
一点一点打出来,加上debug,好艰难 (: 3_~)_
但是...... 最后ac了就好爽啊o(* ̄︶ ̄*)o?(?╯?╰?)?
2019-08-28
以上是关于K-th occurrence(后缀树组+划分树+ST表+RMQ+二分)的主要内容,如果未能解决你的问题,请参考以下文章
HDU - 6704 K-th occurrence (后缀数组+主席树/后缀自动机+线段树合并+倍增)
hdu 6704 K-th occurrence 二分 ST表 后缀数组 主席树
2019CCPC网络预选赛 1003 K-th occurrence 后缀自动机 + 二分 + 主席树
hdu6704 2019CCPC网络选拔赛1003 K-th occurrence 后缀数组