HDU5008 题目链接 VJ上面 hdu上面
题意 给一个串,q次查询里面第k大的字串,并且要求输出这个串最早出现的位置的左右下标值
题解 Tips:看不懂题解的话可以看题解下面的题解小细节(之所以在前面提示是因为小编经常看到一个东西自己想了半天,然后发现后面竟然有解释…所以感觉有点浪费时间,所以自己的博文应该防止自己陷入同样的坑)
解法一 无RMQ O(case*q*(log_(n)+n)
) 考虑找到第k小的子串,直接拿原串先构造后缀数组,统计一下第i个后缀有多少个不同的前缀num[i](也就是在原串中有多少个不重复的子串),按sa排序后,这些连续出现的子串的字典序也是相同的,那么对num[i]求前缀和后就可以去二分一个位置,找到字典序第k小的子串出现的位置pos了(到这里解法二也要用)。这里找到的位置不一定是最靠左的(不理解可以看下面的题解分析),所以还要在原串中找一下最左的位置,其实到了这里,直接向后,暴力遍历后面排名的串(不理解可以看下面的题解分析),若串的最长的连续的height[i]>=目标子串长度,则维护min(L,l)就可以直接得到最小的答案
解法二 RMQ O(case*(n*log_(n)+q*log_(n))
) 当然解法一在极限数组(例如10W个a)很可能会TLE的,所以我们来看更快的方法,以应对更高的要求,把平时的节俭(抠门)习惯在计算机上面发挥到极致
先像解法一前面部分一样确定了当前的位置pos,我们要做的就是在pos后面找个R,使得[pos,R]这个区间的height的最小值>=目标子串的长度,那么找R可以直接在[POS,n]中二分,由于我们的height数组并不是有序的,所以我们不能使用lower_bound,但是要应对多次询问,我们不能像解法一一样暴力了,所以可以使用RMQ,在case开始的时候用n*log_(n)进行预处理,然后在多次查询中享受O(1)带来的极致体验(节俭的生活就是如此地惬意),最后我们在[pos,R]区间再RMQ一下就得到最后的答案了。注意这里求区间的RMQ和求答案的RMQ是查询的两个数组,要分别初始化…
题解细节精讲QA Q1: 为什么后面只要找pos后的后缀中的前缀,不用往前找?而且为什么不同的串是那样求出来的?A:
首先是关于一个字符串有多少不同子串的问题,串由小到大排起序来应该是按照sa[i]的顺序排出来的产生的。 比如abbacd,排序出来的后缀是这样的 rank值i—对应的后缀sa[i]
1—abbacd 第一个串产生的6个前缀 都是新的子串(a,ab,abb……)
2—acd 第二个串除了和上一个串的相同的前缀a(长度为1) 3-1=2 产生了2个子串
3—bacd 4-0=4
4—bbacd 5-1=4
5—cd 2-0=0
6—d 1-0=0
所以所有不同的前缀应该是(len-sa[i])-height[i]的和,即后缀串长(总串长减后缀起始位置)减去与上一个串的最长公共前缀,然后求和。 如果你不了解height数组—>建议看看学习后缀数组的小建议
然后我们可以观察到字串是按照排名过来的 a,ab,abb,abba,abbac,abbacd,ac,acd,b,ba,……
并且也可以观察到第k大的不同的串如果在多个位置出现,那么一定是在后面的串中出现,比如k=3,即abb只能在后面的串出现(在abba,abbac,abbacd中出现)–>所以只要在后面查找 主要原因是所有的不同的串都是每个后缀的前缀
Q2: 为什么我们找到的第一个不是最靠左的呢?A: 这里可以举一个反例就解决了,而且其实我们在题解二也举了这个例子(10w个a),我们这里为了分析方便就举例给的串是aaa,那么 排名rank 对应的后缀串 1 a(rank[1]=2,即是后缀2) 2 aa 3 aaa因此我们就可以看到第一个找到的a不是位置上最左边的,反而是最右边的
AC代码 提交都是G++
解法一 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 #include <bits/stdc++.h> using namespace std;const int maxn=100010 ;typedef long long LL;int sa[maxn],height[maxn],rank[maxn],t[maxn],t2[maxn],c[maxn];int n;char str[maxn];int q;LL sum[maxn]; void build_sa (int m,int n) { int *x=t,*y=t2; for (int i=0 ;i<m;i++)c[i]=0 ; for (int i=0 ;i<n;i++)c[x[i]=str[i]]++; for (int i=1 ;i<m;i++)c[i]+=c[i-1 ]; for (int i=n-1 ;i>=0 ;i--)sa[--c[x[i]]]=i; for (int k=1 ;k<=n;k<<=1 ){ int p=0 ; for (int i=n-k;i<n;i++)y[p++]=i; for (int i=0 ;i<n;i++)if (sa[i]>=k)y[p++]=sa[i]-k; for (int i=0 ;i<m;i++)c[i]=0 ; for (int i=0 ;i<n;i++)c[x[y[i]]]++; for (int i=1 ;i<m;i++)c[i]+=c[i-1 ]; for (int i=n-1 ;i>=0 ;i--)sa[--c[x[y[i]]]]=y[i]; swap (x,y); x[sa[0 ]]=0 ;p=1 ; for (int i=1 ;i<n;i++) x[sa[i]]=(y[sa[i-1 ]]==y[sa[i]]&&y[sa[i-1 ]+k]==y[sa[i]+k]?p-1 :p++); if (p>=n)break ; m=p; } } void getheight (int n) { int k=0 ; for (int i=1 ;i<=n;i++)::rank[sa[i]]=i; for (int i=0 ;i<n;i++){ if (k)k--; int j=sa[::rank[i]-1 ]; while (str[i+k]==str[j+k])k++; height[::rank[i]]=k; } } void process () { memset (sum,0 ,sizeof (sum)); sum[1 ]=n-sa[1 ]; for (int i=2 ;i<=n;i++) sum[i]=sum[i-1 ]+n-sa[i]-height[i]; } void solve () { scanf ("%d" ,&q); LL l=0 ,r=0 ; process (); while (q--){ LL v; scanf ("%lld" ,&v); LL k=(l^r^v)+1 ; int pos=lower_bound (sum+1 ,sum+1 +n,k)-sum; LL tl=sa[pos],tr=n-(sum[pos]-k+1 ); l=tl,r=tr; int len=tr-tl+1 ; while (pos+1 <=n&&height[pos+1 ]>=len){ pos++; tl=sa[pos],tr=tl+len-1 ; l=min (l,tl),r=min (r,tr); } l++,r++; if (pos>=n+1 )l=r=0 ; cout<<l<<" " <<r<<endl; } } int main () { while (scanf ("%s" ,str)!=EOF){ n=strlen (str); build_sa (123 ,n+1 ); getheight (n); solve (); } return 0 ; }
解法二 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 #include <bits/stdc++.h> using namespace std;typedef long long ll;const int maxn=205000 ;char str[maxn];int belong[maxn];int s[maxn],rs[maxn];int sa[maxn],t[maxn],t2[maxn],c[maxn];int n,m,tt;int rank[maxn],height[maxn];int d[2 ][maxn][50 ];int LOG[maxn];ll num[maxn]; int len,l;inline int idx (char c) { return c-'a' +1 ; }inline char fdx (int x) { return char (x-1 +'a' ); }void calheight (int n) { int i,k=0 ; for (i=0 ; i<=n; i++) ::rank[sa[i]]=i; for (i=0 ; i<n; i++){ if (k) k--; int j=sa[::rank[i]-1 ]; while (s[i+k]==s[j+k]) k++; height[::rank[i]]=k; } } void da (int m,int n) { n++; int i,*x=t,*y=t2; for (int i=0 ; i<m; i++) c[i]=0 ; for (int i=0 ; i<n; i++) c[x[i]=s[i]]++; for (int i=1 ; i<m; i++) c[i]+=c[i-1 ]; for (int i=n-1 ; i>=0 ; i--) sa[--c[x[i]]]=i; for (int k=1 ; k<=n; k<<=1 ){ int p=0 ; for (i=n-k; i<n; i++) y[p++]=i; for (i=0 ; i<n; i++) if (sa[i]>=k) y[p++]=sa[i]-k; for (i=0 ; i<m; i++) c[i]=0 ; for (i=0 ; i<n; i++) c[x[y[i]]]++; for (i=1 ; i<m; i++) c[i]+=c[i-1 ]; for (i=n-1 ; i>=0 ; i--) sa[--c[x[y[i]]]] = y[i]; swap (x,y); p=1 ; x[sa[0 ]]=0 ; for (i=1 ; i<n; i++) x[sa[i]]=(y[sa[i-1 ]]==y[sa[i]] && y[sa[i-1 ]+k]==y[sa[i]+k])? p-1 : p++; if (p>=n) break ; m=p; } } int RMQ_init (int x,int A[]) { for (int i=1 ; i<=n; i++) d[x][i][0 ]=A[i]; for (int j=1 ; (1 <<j)<=n; j++) for (int i=1 ; i+(1 <<j)-1 <=n; i++) d[x][i][j]=min (d[x][i][j-1 ],d[x][i+(1 <<(j-1 ))][j-1 ]); return 0 ; } int RMQ (int x,int L,int R) { int k=LOG[R-L+1 ]; return min (d[x][L][k],d[x][R-(1 <<k)+1 ][k]); } int main () { int k=0 ; for (int i=0 ; i<105000 ; i++){ while ((1 <<(k+1 ))<=i) k++; LOG[i]=k; } while (~scanf ("%s" ,str)){ int l=strlen (str); for (int i=0 ; i<l; i++) s[i]=idx (str[i]); n=l; s[n]=0 ; da (33 ,n); calheight (n); for (int i=0 ; i<=n; i++) num[i]=n-sa[i]; for (int i=1 ; i<=n; i++) num[i]-=height[i]; for (int i=1 ; i<=n; i++) num[i]+=num[i-1 ]; ll tot=num[n]; scanf ("%d" ,&m); ll la=0 ,lb=0 ; ll k; RMQ_init (0 ,height); RMQ_init (1 ,sa); while (m--){ scanf ("%lld" ,&k); k=(k^la^lb)+1 ; if (k>=1 && k<=tot){ int pos=lower_bound (num+1 ,num+1 +n,k)-num; int len=k-num[pos-1 ]+height[pos]; int l=pos+1 ,r=n; int mid; int L=pos,R; while (l<r){ mid=(l+r)>>1 ; if (RMQ (0 ,pos+1 ,mid)>=len) l=mid+1 ; else r=mid; } if (RMQ (0 ,pos+1 ,l)>=len) R=l; else R=l-1 ; la=RMQ (1 ,L,R); lb=la+len-1 ;la++;lb++; printf ("%lld %lld\n" ,la,lb); } else la=lb=0 ,puts ("0 0" ); } } return 0 ; }
参考:http://www.voidcn.com/article/p-xboamjdx-bg.html https://www.cnblogs.com/chanme/p/4000976.html
每天一句叨叨 从明天起,做一个幸福的人(每天只玩半个小时的手机,让自己要么大屏高效,要么认真体验生活)
喂马、劈柴,周游世界
从明天起,关心粮食和蔬菜
我有一所房子,面朝大海,春暖花开
从明天起,和每一个亲人通信
告诉他们我的幸福
那幸福的闪电告诉我的
我将告诉每一个人
给每一条河每一座山取一个温暖的名字
陌生人,我也为你祝福
愿你有一个灿烂的前程
愿你有情人终成眷属
愿你在尘世获得幸福
我只愿面朝大海,春暖花开