HDU - 6704 K-th occurrence (后缀数组+主席树/后缀自动机+线段树合并+倍增)

Posted asdfsag

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HDU - 6704 K-th occurrence (后缀数组+主席树/后缀自动机+线段树合并+倍增)相关的知识,希望对你有一定的参考价值。

题意:给你一个长度为n的字符串和m组询问,每组询问给出l,r,k,求s[l,r]的第k次出现的左端点。

解法一:

求出后缀数组,按照排名建主席树,对于每组询问二分或倍增找出主席树上所对应的的左右端点,求第k大的下标即可。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 typedef long long ll;
 4 const int N=1e5+10,mod=998244353;
 5 char buf[N];
 6 int s[N],sa[N],buf1[N],buf2[N],c[N],n,rnk[N],ht[N],ST[N][20],Log[N],m;
 7 void Sort(int* x,int* y,int m) 
 8     for(int i=0; i<m; ++i)c[i]=0;
 9     for(int i=0; i<n; ++i)++c[x[i]];
10     for(int i=1; i<m; ++i)c[i]+=c[i-1];
11     for(int i=n-1; i>=0; --i)sa[--c[x[y[i]]]]=y[i];
12 
13 void da(int* s,int n,int m=1000) 
14     int *x=buf1,*y=buf2;
15     x[n]=y[n]=-1;
16     for(int i=0; i<n; ++i)x[i]=s[i],y[i]=i;
17     Sort(x,y,m);
18     for(int k=1; k<n; k<<=1) 
19         int p=0;
20         for(int i=n-k; i<n; ++i)y[p++]=i;
21         for(int i=0; i<n; ++i)if(sa[i]>=k)y[p++]=sa[i]-k;
22         Sort(x,y,m),p=1,y[sa[0]]=0;
23         for(int i=1; i<n; ++i)y[sa[i]]=x[sa[i-1]]==x[sa[i]]&&x[sa[i-1]+k]==x[sa[i]+k]?p-1:p++;
24         if(p==n)break;
25         swap(x,y),m=p;
26     
27 
28 void getht() 
29     for(int i=0; i<n; ++i)rnk[sa[i]]=i;
30     ht[0]=0;
31     for(int i=0,k=0; i<n; ++i) 
32         if(k)--k;
33         if(!rnk[i])continue;
34         for(; s[i+k]==s[sa[rnk[i]-1]+k]; ++k);
35         ht[rnk[i]]=k;
36     
37 
38 void initST() 
39     for(int i=1; i<n; ++i)ST[i][0]=ht[i];
40     for(int j=1; (1<<j)<=n; ++j)
41         for(int i=1; i+(1<<j)-1<n; ++i)
42             ST[i][j]=min(ST[i][j-1],ST[i+(1<<(j-1))][j-1]);
43 
44 int lcp(int l,int r) 
45     if(l==r)return n-sa[l];
46     if(l>r)swap(l,r);
47     l++;
48     int k=Log[r-l+1];
49     return min(ST[l][k],ST[r-(1<<k)+1][k]);
50 
51 int rt[N],ls[N*30],rs[N*30],sum[N*30],tot;
52 #define mid ((l+r)>>1)
53 int cpy(int v) int u=++tot; ls[u]=ls[v],rs[u]=rs[v],sum[u]=sum[v]; return u;
54 void upd(int& u,int v,int p,int l=0,int r=n-1) 
55     u=cpy(v);
56     sum[u]++;
57     if(l==r)return;
58     p<=mid?upd(ls[u],ls[v],p,l,mid):upd(rs[u],rs[v],p,mid+1,r);
59 
60 int qry(int u,int v,int k,int l=0,int r=n-1) 
61     if(l==r)return l;
62     int cnt=sum[ls[u]]-sum[ls[v]];
63     return k<=cnt?qry(ls[u],ls[v],k,l,mid):qry(rs[u],rs[v],k-cnt,mid+1,r);
64 
65 void build() 
66     da(s,n),getht(),initST();
67     tot=0;
68     for(int i=1; i<=n; ++i)upd(rt[i],rt[i-1],sa[i-1]);
69 
70 int main() 
71     Log[0]=-1;
72     for(int i=1; i<N; ++i)Log[i]=Log[i>>1]+1;
73     int T;
74     for(scanf("%d",&T); T--;) 
75         scanf("%d%d%s",&n,&m,buf);
76         for(int i=0; i<n; ++i)s[i]=buf[i];
77         s[n]=0;
78         build();
79         while(m--) 
80             int l,r,k;
81             scanf("%d%d%d",&l,&r,&k);
82             l--,r--;
83             int L,R,M=rnk[l],len=r-l+1;
84             L=R=M;
85             for(int i=Log[n]; i>=0; --i)if(L-(1<<i)>=0&&lcp(M,L-(1<<i))>=len)L-=(1<<i);
86             for(int i=Log[n]; i>=0; --i)if(R+(1<<i)<n&&lcp(M,R+(1<<i))>=len)R+=(1<<i);
87             printf("%d\n",k<=sum[rt[R+1]]-sum[rt[L]]?qry(rt[R+1],rt[L],k)+1:-1);
88         
89     
90     return 0;
91 

解法二:

建立后缀自动机,对后缀树(fail树)作线段树合并可得到每个结点包含的全部right值。对每个询问倍增找到待查询子串所对应的结点,然后线段树上查询第k大即可。

可持久化合并可以实现在线查询。

fail树上dfs序建可持久化线段树貌似也可以(这句话怎么这么耳熟?)

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 typedef long long ll;
 4 const int N=2e5+10,M=26;
 5 char s[N];
 6 int n,m,fa[N],go[N][M],mxl[N],last,tot,samrt[N],c[N],ss[N],Fa[N][20];
 7 int rt[N],ls[N*50],rs[N*50],sum[N*50],tot2;
 8 #define mid ((l+r)>>1)
 9 int cpy(int v) int u=++tot2; ls[u]=ls[v],rs[u]=rs[v],sum[u]=sum[v]; return u;
10 void upd(int& u,int v,int p,int l=1,int r=n) 
11     u=cpy(v),sum[u]++;
12     if(l==r)return;
13     p<=mid?upd(ls[u],ls[v],p,l,mid):upd(rs[u],rs[v],p,mid+1,r);
14 
15 void mg(int& w,int u,int v) 
16     if(!u||!v) w=u|v; return;
17     w=cpy(u),sum[w]+=sum[v];
18     mg(ls[w],ls[u],ls[v]),mg(rs[w],rs[u],rs[v]);
19 
20 int qry(int u,int k,int l=1,int r=n) 
21     if(l==r)return l;
22     return k<=sum[ls[u]]?qry(ls[u],k,l,mid):qry(rs[u],k-sum[ls[u]],mid+1,r);
23 
24 int newnode(int l) int u=++tot; rt[u]=0,mxl[u]=l,memset(go[u],0,sizeof go[u]); return u;
25 void add(int ch,int r) 
26     int p=last,np=last=newnode(mxl[p]+1);
27     samrt[r]=np;
28     upd(rt[np],rt[np],r,1);
29     for(; p&&!go[p][ch]; p=fa[p])go[p][ch]=np;
30     if(!p)fa[np]=1;
31     else 
32         int q=go[p][ch];
33         if(mxl[q]==mxl[p]+1)fa[np]=q;
34         else 
35             int nq=newnode(mxl[p]+1);
36             memcpy(go[nq],go[q],sizeof go[q]);
37             fa[nq]=fa[q],fa[q]=fa[np]=nq;
38             for(; p&&go[p][ch]==q; p=fa[p])go[p][ch]=nq;
39         
40     
41 
42 void build() 
43     tot=tot2=0,last=newnode(0);
44     for(int i=0; i<n; ++i)add(s[i]-a,i+1);
45     for(int i=0; i<=tot; ++i)c[i]=0;
46     for(int i=1; i<=tot; ++i)++c[mxl[i]];
47     for(int i=1; i<=tot; ++i)c[i]+=c[i-1];
48     for(int i=1; i<=tot; ++i)ss[--c[mxl[i]]]=i;
49     for(int i=tot-1; i>0; --i)mg(rt[fa[ss[i]]],rt[fa[ss[i]]],rt[ss[i]]);
50     for(int i=1; i<=tot; ++i)Fa[i][0]=fa[i];
51     for(int k=1; k<20; ++k)for(int i=1; i<=tot; ++i)Fa[i][k]=Fa[Fa[i][k-1]][k-1];
52 
53 int main() 
54     int T;
55     for(scanf("%d",&T); T--;) 
56         scanf("%d%d%s",&n,&m,s);
57         build();
58         while(m--) 
59             int l,r,k;
60             scanf("%d%d%d",&l,&r,&k);
61             int u=samrt[r],len=r-l+1;
62             if(mxl[fa[u]]+1>len) 
63                 for(int k=19; k>=0; --k)if(mxl[fa[Fa[u][k]]]+1>len)u=Fa[u][k];
64                 u=fa[u];
65             
66             printf("%d\n",k<=sum[rt[u]]?qry(rt[u],k)-len+1:-1);
67         
68     
69     return 0;
70 

 

以上是关于HDU - 6704 K-th occurrence (后缀数组+主席树/后缀自动机+线段树合并+倍增)的主要内容,如果未能解决你的问题,请参考以下文章

HDU - 6704 K-th occurrence (后缀数组+主席树/后缀自动机+线段树合并+倍增)

hdu6704 2019CCPC网络选拔赛1003 K-th occurrence 后缀数组

K-th occurrence(后缀树组+划分树+ST表+RMQ+二分)

hdu6704 后缀数组+主席树+ST +二分

hdu 6231 -- K-th Number(二分+尺取)

HDU - 6231:K-th Number (不错的二分)