标签:图片 mes lap build c++ 维护 etl 二分 kmp
根据题意,寻找子串出现的第k次的开头。寻找第k大,一般可以想到用主席树来维护。
但是这题还需要更多的转化,首先想到我们如果想知道子串匹配,一个可以考虑kmp,但是因为询问过多,不太科学。
一般还有两种,一种是哈希算法,一种是后缀数组求lcp。考虑哈希算法,感觉可做性不是很大,因为他要多次匹配。考虑后缀数组
我们发现一个重要的信息,也就是后缀数组的height数组是随着位置的远离而变小的,因此每次的询问其实都有一段固定的区间。这个区间可以通过两次二分求取。
这样我们的任务就变成了在后缀数组的排名序列上,二分出一段满足条件的区间,我们在这上面求取第k大值,后者可以用主席树维护。
#includeusing namespace std;
using namespace std;
typedef long long ll;
const int N=1e5+10;
int n,q;
char t[N];
int sa[N],rk[N],od[N],cnt[N],id[N],px[N];
ll h[N];
int root[N];
ll f[N][26];
ll lg[N],idx;
struct node{
int l,r;
int cnt;
}tr[40*N];
bool cmp(int x, int y, int w){
return od[x]==od[y]&&od[x+w]==od[y+w];
}
void build(int &rt,int l,int r){
rt=++idx;
tr[rt]={l,r,0};
if(l==r)
return;
int mid=l+r>>1;
build(tr[rt].l,l,mid);
build(tr[rt].r,mid+1,r);
}
void da(int n,int m){
int i;
memset(cnt,0,sizeof cnt);
for(i=1;i;
for(i=1;i1];
for(i=n;i>=1;i--) sa[cnt[rk[i]]--]=i;
int p;
for(int w=1;w1,m=p){
p=0;
for(i=n;i>n-w;i--)
id[++p]=i;
for(i=1;i)
if(sa[i]>w)
id[++p]=sa[i]-w;
memset(cnt,0,sizeof cnt);
for(i=1;i;
for(i=1;i1];
for(i=n;i>=1;i--) sa[cnt[px[i]]--]=id[i];
for(i=0;irk[i];
for(p=0,i=1;i){
rk[sa[i]]=cmp(sa[i-1],sa[i],w)?p:++p;
}
}
}
void st(int n){
int i,j;
for(i=1;i)
f[i][0]=h[i];
lg[1]=0;
for(i=2;i>1]+1;
for(j=1;j){
for(i=1;i+(11){
f[i][j]=min(f[i][j-1],f[i+(11)][j-1]);
}
}
}
int query(int l,int r){
if(l>r)
swap(l,r);
l++;
int len=lg[r-l+1];
return min(f[l][len],f[r-(11][len]);
}
void get_height(int n){
int i;
for(i=1;i){
if(rk[i]==1)
continue;
int a=max(h[rk[i-1]]-1,1ll*0);
while(i+a1]+a])
a++;
h[rk[i]]=a;
}
st(n);
}
int getL(int l,int r,int len,int x){
while(lr){
int mid=l+r>>1;
if(query(mid,x)>=len){
r=mid;
}
else{
l=mid+1;
}
}
return l;
}
int getR(int l,int r,int len,int x){
while(lr){
int mid=l+r+1>>1;
if(query(mid,x)>=len)
l=mid;
else
r=mid-1;
}
return l;
}
int getans(int p,int q,int l,int r,int k){
if(l==r)
return l;
int cnt=tr[tr[q].l].cnt-tr[tr[p].l].cnt;
int mid=l+r>>1;
if(kcnt)
return getans(tr[p].l,tr[q].l,l,mid,k);
else
return getans(tr[p].r,tr[q].r,mid+1,r,k-cnt);
}
int solve(int l,int r,int k){
int len=r-l+1;
int L=getL(1,rk[l],len,rk[l]);
int R=getR(rk[l],n,len,rk[l]);
if(k>R-L+1)
return -1;
return getans(root[L-1],root[R],1,n,k);
}
void add(int &rt,int l,int r,int p,int pos,int k){
rt=++idx;tr[rt]=tr[p];
if(l==r){
tr[rt].cnt+=k;
return ;
}
int mid=l+r>>1;
if(posmid) add(tr[rt].l,l,mid,tr[p].l,pos,k);
else add(tr[rt].r,mid+1,r,tr[p].r,pos,k);
tr[rt].cnt=tr[tr[rt].l].cnt+tr[tr[rt].r].cnt;
}
int main(){
//ios::sync_with_stdio(false);
int T;
cin>>T;
while(T--){
scanf("%d%d",&n,&q);
scanf("%s",t+1);
int lent=strlen(t+1);
da(lent,150);
get_height(lent);
idx=0;
build(root[0],1,n);
for(int i=1;i){
int pos=sa[i];
add(root[i],1,n,root[i-1],pos,1);
}
while(q--){
int l,r,k;
scanf("%d%d%d",&l,&r,&k);
coutendl;
}
}
}
View Code
HDU6704 K-th occurrence(后缀数组+二分+主席树)
标签:图片 mes lap build c++ 维护 etl 二分 kmp
原文地址:https://www.cnblogs.com/ctyakwf/p/13674576.html