后缀数组 hash求LCP BZOJ 4310: 跳蚤

Posted cyz666

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了后缀数组 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 }
Assassin

 

以上是关于后缀数组 hash求LCP BZOJ 4310: 跳蚤的主要内容,如果未能解决你的问题,请参考以下文章

bzoj3796

BZOJ4566找相同字符(后缀数组)

bzoj4310跳蚤 后缀数组+二分

BZOJ4556字符串(后缀数组,主席树)

BZOJ_1014_[JSOI2008]_火星人prefix_(Splay+LCP_Hash+二分)

bzoj1014: [JSOI2008]火星人prefix(splay+hash+二分)