Codeforces1037H Security【贪心+后缀自动机线段树合并维护endpos集合】

题目描述:

在这里插入图片描述
洛谷链接

题目分析:

要求 S 1 S_1 S1字典序尽量小,又要严格大于 T T T。那么最优肯定是尽可能前面一段与 T T T相同,然后下一个字符大于 T T T

又因为 S 1 S_1 S1 S S S的子串,所以可以建出 S S S的后缀自动机,然后找到 T T T的前缀能够匹配的最长长度 l e n len len,那么对于 i ∈ [ 1 , l e n + 1 ] i\in[1,len+1] i[1,len+1],我们想要找到最大的一个 i i i,使得 S 1 [ 1 , i − 1 ] = T [ 1 , i − 1 ] S_1[1,i-1]=T[1,i-1] S1[1,i1]=T[1,i1] S [ i ] > T [ i ] S[i]>T[i] S[i]>T[i]

直接从大到小枚举 i i i,然后枚举后缀自动机上一条 > T [ i ] >T[i] >T[i]的转移链,如果其对应的串在 [ l , r ] [l,r] [l,r]中出现了,那么就找到了答案。

判断后缀自动机上一个节点代表的长度为 k k k 的串是否在一个区间出现,只需要它的 e n d p o s endpos endpos集合包含 [ l + k − 1 , r ] [l+k-1,r] [l+k1,r] 中的点,在 f a i l fail fail树上线段树可持久化线段树合并即可。

Code:

#include<bits/stdc++.h>
#define maxn 200005
#define maxp maxn*18
using namespace std;
int n,m,rt[maxn],lc[maxp],rc[maxp],sz;
char s[maxn];
void ins(int &i,int l,int r,int x){
	i=++sz;
	if(l==r) return;
	int mid=(l+r)>>1;
	x<=mid?ins(lc[i],l,mid,x):ins(rc[i],mid+1,r,x);
}
int merge(int x,int y,int l,int r){
	if(!x||!y||l==r) return x+y;
	int i=++sz,mid=(l+r)>>1;
	lc[i]=merge(lc[x],lc[y],l,mid),rc[i]=merge(rc[x],rc[y],mid+1,r);
	return i;
}
bool qry(int i,int l,int r,int x,int y){
	if(!i) return 0;
	if(x<=l&&r<=y) return 1;
	int mid=(l+r)>>1;
	if(x<=mid&&qry(lc[i],l,mid,x,y)) return 1;
	if(y>mid&&qry(rc[i],mid+1,r,x,y)) return 1;
	return 0;
}
namespace SAM{
	int fa[maxn]={-1},ch[maxn][26],len[maxn],last,sz;
	void extend(int c,int pos){
		int cur=++sz,p=last,q; len[last=cur]=len[p]+1,ins(rt[cur],1,n,pos);
		for(;~p&&!ch[p][c];p=fa[p]) ch[p][c]=cur;
		if(p==-1) fa[cur]=0;
		else if(len[q=ch[p][c]]==len[p]+1) fa[cur]=q;
		else{
			int clone=++sz; len[clone]=len[p]+1,memcpy(ch[clone],ch[q],sizeof ch[q]);
			fa[clone]=fa[q],fa[cur]=fa[q]=clone;
			for(;~p&&ch[p][c]==q;p=fa[p]) ch[p][c]=clone;
		}
	}
}
using SAM::ch;
vector<int>G[maxn];
void dfs(int u){for(int i=G[u].size()-1,v;i>=0;i--) dfs(v=G[u][i]),rt[u]=merge(rt[u],rt[v],1,n);}
int main()
{
	scanf("%s%d",s+1,&m),n=strlen(s+1);
	for(int i=1;i<=n;i++) SAM::extend(s[i]-'a',i);
	for(int i=1;i<=SAM::sz;i++) G[SAM::fa[i]].push_back(i);
	dfs(0),rt[0]=0;
	static int stk[maxn],tp;
	for(int l,r,len;m--;){
		scanf("%d%d%s",&l,&r,s+1),len=strlen(s+1),stk[tp=1]=0,s[len+1]='a'-1;
		for(int i=1,p=0;i<=len&&ch[p][s[i]-'a'];i++) stk[++tp]=p=ch[p][s[i]-'a'];
		bool flg=0;
		for(int i=min(r-l+1,tp);i>=1&&!flg;i--)
			for(int j=s[i]+1-'a';j<26;j++)
				if(qry(rt[ch[stk[i]][j]],1,n,l+i-1,r)){
					for(int k=1;k<i;k++) putchar(s[k]);
					putchar(j+'a'),putchar('\n'),flg=1; break;
				}
		if(!flg) puts("-1");
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值