大意: 给定串$s$, $q$个询问$(l,r,k)$, 求子串$s[l,r]$的第$k$次出现位置.
本来是个简单签到题, 可惜比赛的时候还没学$SA$...... 好亏啊
相同的子串在$SA$中是一定是连续的一段$[L,R]$
满足对于$L<i\le R$都有$h_i\ge r-l+1$
可以先用线段树二分出$L,R$, 然后主席树查询第$k$大即可
#include <iostream> #include <algorithm> #include <cstdio> #include <cstring> #define REP(i,a,n) for(int i=a;i<=n;++i) #define PER(i,a,n) for(int i=n;i>=a;--i) #define lc (o<<1) #define rc (lc|1) #define mid ((l+r)>>1) #define ls lc,l,mid #define rs rc,mid+1,r using namespace std; const int N = 1e5+10; int n, q, tot, a[N], T[N]; struct {int l,r,v;} tr[N*40]; char s[N]; int c[N],rk[N],h[N],sa[N],mi[N<<2]; void build(int *a, int n, int m) { a[n+1] = 0; int i,*x=rk,*y=h; for(i=1;i<=m;i++) c[i]=0; for(i=1;i<=n;i++) c[x[i]=a[i]]++; for(i=1;i<=m;i++) c[i]+=c[i-1]; for(i=n;i;i--) sa[c[x[i]]--]=i; for(int k=1,p;k<=n;k<<=1) { p=0; for(i=n-k+1;i<=n;i++) y[++p]=i; for(i=1;i<=n;i++) if(sa[i]>k) y[++p]=sa[i]-k; for(i=1;i<=m;i++) c[i]=0; for(i=1;i<=n;i++) c[x[y[i]]]++; for(i=1;i<=m;i++) c[i]+=c[i-1]; for(i=n;i;i--) sa[c[x[y[i]]]--]=y[i]; swap(x,y); x[sa[1]]=1; p=1; for(i=2;i<=n;i++) x[sa[i]]=(y[sa[i-1]]==y[sa[i]] && y[sa[i-1]+k]==y[sa[i]+k])?p:++p; if(p==n) break; m=p; } for(i=1;i<=n;i++) rk[sa[i]]=i; for(int i=1,j,k=0;i<=n;i++){ if(k) k--; j=sa[rk[i]-1]; while (a[i+k]==a[j+k]) k++; h[rk[i]] = k; } } //求最小的位置p, 使得[p,x]的最小值>=v int find1(int o, int l, int r, int x, int v) { if (r<=x) { if (l==r) return mi[o]>=v?l:-1; if (mi[rc]<v) return find1(rs,x,v); int t = find1(ls,x,v); return t==-1?mid+1:t; } if (mid>=x) return find1(ls,x,v); int R = find1(rs,x,v); if (R==-1||R>mid+1) return R; int L = find1(ls,x,v); return L==-1?R:L; } //求找最大的位置p, 使得[x,p]的最小值>=v int find2(int o, int l, int r, int x, int v) { if (x<=l) { if (l==r) return mi[o]>=v?l:-1; if (mi[lc]<v) return find2(ls,x,v); int t = find2(rs,x,v); return t==-1?mid:t; } if (mid<x) return find2(rs,x,v); int L = find2(ls,x,v); if (L==-1||L<mid) return L; int R = find2(rs,x,v); return R==-1?L:R; } int query(int u, int v, int l, int r, int k) { if (l==r) return l; int s = tr[tr[v].l].v-tr[tr[u].l].v; if (s>=k) return query(tr[u].l,tr[v].l,l,mid,k); return query(tr[u].r,tr[v].r,mid+1,r,k-s); } void add(int &o, int l, int r, int x) { tr[++tot]=tr[o],o=tot,++tr[o].v; if (l!=r) mid>=x?add(tr[o].l,l,mid,x):add(tr[o].r,mid+1,r,x); } int query(int p, int len, int k) { int l = p>1?find1(1,2,n,p,len)-1:1; int r = p<n?find2(1,2,n,p+1,len):n; if (l<0) l = p; if (r<0) r = p; if (r-l+1>=k) return query(T[l-1],T[r],1,n,k); return -1; } void build2(int o, int l, int r) { if (l==r) return mi[o]=h[l],void(); build2(ls),build2(rs); mi[o]=min(mi[lc],mi[rc]); } void brute_force() { while (q--) { int l, r, k; scanf("%d%d%d",&l,&r,&k); string g(s+l,s+r+1); int pos = -1, cnt = 0; REP(i,1,n) if (string(s+i,s+i+r-l+1)==g) { if (++cnt==k) { pos = i; break; } } printf("%d\n", pos); } } int main() { int t; scanf("%d", &t); while (t--) { scanf("%d%d%s", &n, &q, s+1); if (n<=10) {brute_force();continue;} REP(i,1,n) a[i]=s[i]-‘a‘+1; build(a,n,26); build2(1,2,n); REP(i,1,n) { T[i] = T[i-1]; add(T[i],1,n,sa[i]); } while (q--) { int l, r, k; scanf("%d%d%d", &l, &r, &k); int len = r-l+1; printf("%d\n", query(rk[l],r-l+1,k)); } REP(i,0,n) T[i]=0; while (tot) tr[tot].l=tr[tot].r=tr[tot].v=0,--tot; } }
原文地址:https://www.cnblogs.com/uid001/p/11405505.html
时间: 2024-10-10 04:51:13