把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【CF1037H】Security(后缀自动机+线段树合并)

点此看题面

大致题意: 给定一个字符串\(S\),每次询问\(S[l,r]\)中字典序严格大于询问串\(T\)的字典序最小的子串。

前言

下午花了一个多小时口胡了五六道后缀自动机水题。

本来懒得打了,后来想想很久没写过后缀自动机+线段树合并这玩意了,最终决定写一发。

结果果然挂了。。。数组开小这种事真的是。。。

大致思路

考虑求出字典序要严格大于,因此我们只要从大到小枚举答案串与询问串有多少位相同,然后找出严格大于询问串下一位字符的最小字符即可。

对于之前相同的位,我们可以利用后缀自动机,找到对应的节点。

然后对于下一位字符,我们去枚举这一字符,然后判断是否存在。

考虑题目中询问的是一段区间,因此我们要判断当前节点该子节点是否存在属于该区间范围内的子串。

那么只要用线段树合并维护每个点的\(endpos\)集合即可。

代码

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 100000
#define E 200000
#define LN 20
using namespace std;
int n,l;char s[E+5];
class SegmentTree//线段树合并
{
	private:
		#define PT CI l=1,CI r=n
		#define LT S[rt][0],l,mid
		#define RT S[rt][1],mid+1,r
		int Nt,S[N*LN<<2][2];
	public:
		I void U(CI x,CI v,int& rt,PT)//插入一个点
		{
			RI mid=l+r>>1;!rt&&(rt=++Nt),l==r||(x<=mid?U(x,v,LT):U(x,v,RT),0);
		}
		I int Q(CI L,CI R,CI rt,PT)//询问是否存在区间[L,R]中的点
		{
			RI mid=l+r>>1;return rt&&((L<=l&&r<=R)||(L<=mid&&Q(L,R,LT))||(R>mid&&Q(L,R,RT)));
		}
		I void M(int& x,CI y,PT)//线段树合并
		{
			if(!x||!y) return (void)(x|=y);if(l==r) return;RI mid=l+r>>1;S[++Nt][0]=S[x][0],
			S[Nt][1]=S[x][1],M(S[x=Nt][0],S[y][0],l,mid),M(S[x][1],S[y][1],mid+1,r);
		}
}T;
class SuffixAutomation//后缀自动机
{
	private:
		int Nt,lst,t[2*N+5],v[2*N+5];struct node {int Rt,L,F,S[30];}O[2*N+5];
		I bool Q(CI rt,CI p,CI x,CI y)//询问,采用递归形式,实现从大到小枚举相同长度
		{
			if(!rt) return 0;RI t=s[p]&31;if(p<=l&&Q(O[rt].S[t],p+1,x,y)) return 1;//先继续搜高位
			for(RI i=t+1;i<=26;++i) if(T.Q(x+p-1,y,O[O[rt].S[i]].Rt))//枚举下位,线段树上询问
				{for(RI k=1;k^p;++k) putchar(s[k]);putchar(96|i),putchar('\n');return 1;}//找到合法答案,输出
			return 0;//没找到
		}
	public:
		I SuffixAutomation() {Nt=lst=1;}
		I void Ins(CI id,CI x)//后缀自动机插入字符
		{
			RI p=lst,o=lst=++Nt;O[o].L=O[p].L+1,T.U(id,1,O[o].Rt);
			W(p&&!O[p].S[x]) O[p].S[x]=o,p=O[p].F;if(!p) return (void)(O[o].F=1);
			RI q=O[p].S[x];if(O[q].L==O[p].L+1) return (void)(O[o].F=q);
			RI k=++Nt;(O[k]=O[q]).L=O[p].L+1,O[o].F=O[q].F=k;
			W(p&&O[p].S[x]==q) O[p].S[x]=k,p=O[p].F;
		}
		I void Work()//统计信息
		{
			RI i;for(i=1;i<=Nt;++i) ++t[O[i].L];for(i=1;i<=n;++i) t[i]+=t[i-1];
			for(i=Nt;i;--i) v[t[O[i].L]--]=i;for(i=Nt;i^1;--i) T.M(O[O[v[i]].F].Rt,O[v[i]].Rt);//合并线段树
		}
		I void Qry(CI x,CI y) {l>y-x+1&&(l=y-x+1),!Q(1,1,x,y)&&puts("-1");}//找不到输出-1
}S;
int main()
{
	RI Qt,i;for(scanf("%s",s+1),n=strlen(s+1),i=1;i<=n;++i) S.Ins(i,s[i]&31);S.Work();//初始化
	RI x,y;scanf("%d",&Qt);W(Qt--) scanf("%d%d%s",&x,&y,s+1),l=strlen(s+1),S.Qry(x,y);return 0;//处理询问
}
posted @ 2020-06-16 15:15  TheLostWeak  阅读(134)  评论(0编辑  收藏  举报