后缀数组 hash求LCP BZOJ 4310: 跳蚤
后缀数组的题博客里没放进去过。。所以挖了一题写写 充实下博客 顺便留作板子。。
一个字符串S中 内容不同的子串 有 sigma{n-sa[i]+1-h[i]} (噢 这里的h[]就是大家熟知的height[])
所以l=1,r=上述sigma 二分 答案是字典序第几大的子串。
然后 求S中第k大的子串W : 因为h[i]是与i-1有关的 所以要从n downto 1,k-=n-sa[i]+1-h[i] 至 k再减就非正了
显然这样扫过来 子串字典序是递减的 因此可以得到第k大子串W
然后再贪心 n downto 1 若遇到比 W大的子串 就划分,验证 当前二分的这个第k大可不可行
判断 比W大还是小 用hash 二分求LCP即可
1 #include <bits/stdc++.h> 2 #define N 200005 3 #define LL long long 4 using namespace std; 5 const LL mo=1000000007; 6 int F[27],a[N],rank[N],sa[N],h[N],g1[N],g2[N],next[N],n,m,ans,k,t,l,r,mid,x,y,z; 7 char S[N]; LL f[N],w[N]; 8 int oh(int k,int t,int p,int q){ 9 int l=0,r=min(t-k,q-p)+1,j; 10 while (l<r){ 11 j=l+r+1>>1; 12 (f[k+j-1]-f[k-1]*w[j]%mo+mo)%mo==(f[p+j-1]-f[p-1]*w[j]%mo+mo)%mo? 13 l=j:r=j-1; 14 } 15 if (k+l>t) return 0; 16 if (p+l>q) return 1; 17 return a[k+l]>a[p+l]; 18 } 19 int jud(int u){ 20 int p,q,k,t; 21 for (int i=n;i;--i) 22 if (n-sa[i]+1-h[i]<u) u-=n-sa[i]+1-h[i]; 23 else {p=sa[i];q=n-u+1;break;} 24 t=n; k=1; 25 for (int i=n;i;) 26 if (oh(i,t,p,q)){ 27 if (i==t) return 0; 28 t=i; ++k; 29 } else --i; 30 if (k>m) return 0; return 1; 31 } 32 int main(){ 33 scanf("%d",&m); scanf("%s",S+1); n=strlen(S+1); 34 w[0]=1; 35 for (int i=1;i<=n;++i) { 36 a[i]=S[i]-'a'+1; 37 F[a[i]]=1; 38 f[i]=(f[i-1]*29+a[i])%mo; 39 w[i]=w[i-1]*29%mo; 40 } 41 for (int i=2;i<=26;++i) F[i]+=F[i-1]; 42 for (int i=1;i<=n;++i) rank[i]=F[a[i]]; t=F[26]; 43 for (int m=1;m<n;m<<=1){ 44 for (int i=1;i<=n;++i){ 45 next[i]=g1[rank[i+m]]; 46 g1[rank[i+m]]=i; 47 } 48 for (int i=t;i>=0;--i){ 49 for (int j=g1[i];j;j=k){ 50 k=next[j]; next[j]=g2[rank[j]]; g2[rank[j]]=j; 51 } 52 g1[i]=0; 53 } 54 z=0; 55 for (int i=1;i<=t;++i){ 56 y=-1; 57 for (int j=g2[i];j;j=k){ 58 k=next[j]; next[j]=0; 59 if (y!=rank[j+m]) y=rank[j+m],++z; 60 h[j]=z; 61 } 62 g2[i]=0; 63 } 64 t=z; 65 for (int i=1;i<=n;++i) rank[i]=h[i]; 66 } 67 for (int i=1;i<=n;++i) sa[rank[i]]=i,h[i]=0; 68 for (int i=1,k=0;i<=n;++i) 69 if (rank[i]!=1){ 70 if (k) --k; 71 while (a[i+k]==a[sa[rank[i]-1]+k]) ++k; 72 h[rank[i]]=k; r+=n-i+1-k; 73 } 74 l=1; ++r; 75 while (l<r){ 76 k=l+r+1>>1; 77 jud(k)?l=k:r=k-1; 78 } 79 for (int i=n;i;--i) 80 if (n-sa[i]+1-h[i]<l) l-=n-sa[i]+1-h[i]; 81 else {k=sa[i];t=n-l+1;break;} 82 for (int i=k;i<=t;++i) printf("%c",a[i]+'a'-1); 83 return 0; 84 }
转载请标明出处 http://www.cnblogs.com/cyz666/