Hdu 6704 K-th occurrence【SAM+线段树合并+树上倍增】

时间:2020-04-11
本文章向大家介绍Hdu 6704 K-th occurrence【SAM+线段树合并+树上倍增】,主要包括Hdu 6704 K-th occurrence【SAM+线段树合并+树上倍增】使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

传送门
之前用后缀数组+主席树写过:之前的版本
学习了后缀自动机和线段树合并之后再来做一下这道题。
首先对字符串 \(s\) 建后缀自动机,记录下每个前缀的 endpos 和前缀在 SAM 上所处的位置。
因为 \(s\) 的子串唯一对应 SAM 上的某一点,这个询问相当于问 parent 树上某一点的子树中第 k 大的 endpos 是多少。
考虑怎么快速确定询问串对应 SAM 上的哪一点,根据 parent 树的性质,\(s\) 的前缀对应 parent 树的叶节点,因为已知前缀 \(s_{1,r}\) 所处位置 \(x\),所以让 \(x\) 不断向上跳,直到 \(len[fa[x]]<r-l+1\le len[x]\),此时 \(x\) 就包含了子串 \(s_{l,r}\),当然这个过程就用树上倍增就可以了。
而询问子树上第 k 大值,可以用线段树合并,也可以用 dfs 序将树拉成区间然后建主席树。
所以后缀自动机就是比后缀数组更灵活,既可以套树算法,也可以拉成区间套区间算法。

#include <bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,m;
char s[N];

struct SegTrees{
	#define mid (l+r>>1)
	int sum[N*50],ls[N*50],rs[N*50],tot;
	void clear(){
		memset(sum,0,sizeof(sum));
		memset(ls,0,sizeof(ls));
		memset(rs,0,sizeof(rs));
		tot=0;
	}
	void upd(int &id,int l,int r,int pos){
		if(!id) id=++tot;
		if(l==r) {sum[id]++;return;}
		if(pos<=mid) upd(ls[id],l,mid,pos);
		else upd(rs[id],mid+1,r,pos);
		sum[id]=sum[ls[id]]+sum[rs[id]];
	}
	int merge(int x,int y,int l,int r){
		if(!x||!y) return x+y;
		int id=++tot;
		if(l==r) {sum[id]=sum[x]+sum[y];return id;}
		ls[id]=merge(ls[x],ls[y],l,mid);
		rs[id]=merge(rs[x],rs[y],mid+1,r);
		sum[id]=sum[ls[id]]+sum[rs[id]];
		return id;
	}
	int ask(int id,int l,int r,int k){
		if(l==r) return l;
		if(k<=sum[ls[id]]) return ask(ls[id],l,mid,k);
		else return ask(rs[id],mid+1,r,k-sum[ls[id]]);
	}
	#undef mid
}trs;

struct SuffixAutoMachine{
	int last,tot,len[N*2],fa[N*2],ch[N*2][26],ep[N*2],tpos[N],st[N*2][19];
	int head[N*2],to[N*2],nxt[N*2],rt[N*2],total;
	void add(int u,int v){to[++total]=v;nxt[total]=head[u];head[u]=total;}
	int newnode(int x){fa[++tot]=fa[x];len[tot]=len[x];memcpy(ch[tot],ch[x],sizeof(ch[tot]));return tot;}
	void append(int c,int pos){
		int p=last,np=last=newnode(0);
		len[np]=len[p]+1;ep[np]=pos;tpos[pos]=np;
		for(;p&&!ch[p][c];p=fa[p]) ch[p][c]=np;
		if(!p) {fa[np]=1;return;}
		int q=ch[p][c];
		if(len[q]==len[p]+1) {fa[np]=q;return;}
		int nq=newnode(q);ep[nq]=0;len[nq]=len[p]+1;
		fa[q]=fa[np]=nq;
		for(;p&&ch[p][c]==q;p=fa[p]) ch[p][c]=nq;
	}
	void dfs(int u){
		if(ep[u]) trs.upd(rt[u],1,n,ep[u]);
		for(int i=head[u];i;i=nxt[i]){
			dfs(to[i]);
			rt[u]=trs.merge(rt[u],rt[to[i]],1,n);
		}
	}
	void init(){
		last=tot=total=0;
		last=tot=newnode(0);
		memset(head,0,sizeof(head));
		memset(rt,0,sizeof(rt));
		trs.clear();
		for(int i=1;i<=n;i++) append(s[i]-'a',i);
		for(int i=2;i<=tot;i++) add(fa[i],i),st[i][0]=fa[i];
		dfs(1);
		for(int i=1;i<19;i++) for(int j=1;j<=tot;j++) st[j][i]=st[st[j][i-1]][i-1];
	}
	void ask(int l,int r,int k){
		int p=tpos[r];
		for(int i=18;i>=0;i--) if(len[st[p][i]]>=r-l+1) p=st[p][i];
		if(trs.sum[rt[p]]<k) {printf("-1\n");return;}
		printf("%d\n",trs.ask(rt[p],1,n,k)-(r-l));
	}
}sam;

void solve(){
	scanf("%d%d",&n,&m);
	scanf("%s",s+1);
	sam.init();
	for(int i=1,l,r,k;i<=m;i++){
		scanf("%d%d%d",&l,&r,&k);
		sam.ask(l,r,k);
	}
}

int main(){
	int T;scanf("%d",&T);
	while(T--) solve();
	return 0;
}

原文地址:https://www.cnblogs.com/BakaCirno/p/12679997.html