一、题目
二、解法
既然是要求字典序严格大于模式串又要最小,答案肯定长这样:模式串的一段+比模式串大的一个字符。
我们把模式串放在原串的后缀自动机上匹配,要保证能够匹配并且匹配到的是在 [ l , r ] [l,r] [l,r]区间中的子串,所以我们需要维护出一个 e n d p o s endpos endpos集合,用于判断上面那个,可以用线段树合并的方法维护。如果一个点的 e n d p o s endpos endpos在 [ l − l e n + 1 , r ] [l-len+1,r] [l−len+1,r]处有值,就可以判断它在 [ l , r ] [l,r] [l,r]中出现过了。然后我们枚举比模式串大的一个字符,用上面的方法判断合法性,存下答案即可。
时间复杂度 O ( 26 × n log n ) O(26\times n\log n) O(26×nlogn),如果有一些细节没有懂,可以看我的代码。
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int M = 200005;
int read()
{
int x=0,flag=1;
char c;
while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x*flag;
}
int n,m,tot,f[M],cnt,last,len[M],fa[M],ch[M][26];
char s[M];
int Index,rt[M],ls[20*M],rs[20*M],nxt[M];
struct edge
{
int v,next;
edge(int V=0,int N=0) : v(V) , next(N) {}
} e[2*M];
void ins(int &x,int l,int r,int id)
{
x=++Index;//我们不需要维护其他的东西,有编号就表示出现过
if(l==r) return ;
int mid=(l+r)>>1;
if(mid>=id) ins(ls[x],l,mid,id);
else ins(rs[x],mid+1,r,id);
}
void add(int c)
{
int p=last,np=last=++cnt;
ins(rt[np],1,n,len[np]=len[p]+1);//初始插入
for(; p && !ch[p][c]; p=fa[p]) ch[p][c]=np;
if(!p) fa[np]=1;
else
{
int q=ch[p][c];
if(len[q]==len[p]+1) fa[np]=q;
else
{
int nq=++cnt;
fa[nq]=fa[q];
memcpy(ch[nq],ch[q],sizeof ch[q]);
len[nq]=len[p]+1;
fa[q]=fa[np]=nq;
for(; p && ch[p][c]==q; p=fa[p]) ch[p][c]=nq;
}
}
}
int merge(int x,int y)
{
if(!x || !y) return x|y;
int p=++Index;//新建节点放合并结果
ls[p]=merge(ls[x],ls[y]);
rs[p]=merge(rs[x],rs[y]);
return p;
}
int ask(int i,int l,int r,int L,int R)//查询
{
if(!i || L>r || l>R || L>R) return 0;
if(L<=l && r<=R) return 1;
int mid=(l+r)>>1;
return ask(ls[i],l,mid,L,R)|ask(rs[i],mid+1,r,L,R);
}
void dfs(int u)
{
for(int i=f[u]; i; i=e[i].next)
{
int v=e[i].v;
if(v==fa[u]) continue;
dfs(v);
rt[u]=merge(rt[u],rt[v]);//线段树合并
}
}
int main()
{
cnt=last=1;
scanf("%s",s),n=strlen(s);
for(int i=0; i<n; i++) add(s[i]-'a');
for(int i=2; i<=cnt; i++)
{
e[++tot]=edge(fa[i],f[i]),f[i]=tot;
e[++tot]=edge(i,f[fa[i]]),f[fa[i]]=tot;
}
dfs(1);
m=read();
while(m--)
{
int l=read(),r=read(),i=1,p=1,len=0;
scanf("%s",s+1),len=strlen(s+1);
for(;; i++) //枚举答案的长度
{
nxt[i]=-1;//存答案的数组,表示长度为i的答案的末位
for(int j=max(0,s[i]-'a'+1); j<26; j++) //尝试填入一个比模式串大的字符
if(ch[p][j] && ask(rt[ch[p][j]],1,n,l+i-1,r))
{
nxt[i]=j;
break;
}
if(i==len+1) break;
p=ch[p][s[i]-'a'];
if(!p || !ask(rt[p],1,n,l-i+1,r)) break;
}
while(i && nxt[i]==-1) i--;
if(!i) puts("-1");
else
{
for(int j=1; j<i; j++) printf("%c",s[j]);
printf("%c\n",nxt[i]+'a');
}
}
}