题目描述:
给你一个长度为n的字符串S,你需要维护这个字符串S并支持两种操作:
1、在字符串S末尾插入一个字符。
2、记字符串T为字符串S从第 l 个字符到第 r 个字符所构成的子串。询问字符串T中最长的子串使得该子串在T中出现过至少两次(例如:T=”ababa”,最长的子串应为aba,长度为3),并输出它的长度。如果不存在这样的子串,则输出0。
强制在线,n,m<=50000。
解题思路:
不考虑插入的话。。。我们从1到n加入每个字符。。然后在加入第r个字符的时候回答所有形如[l:r]的询问。。以下的当前串均指s[1:r]。。
后缀自动机的parent树,记根到节点x形成的字符串为str[x]
考虑在每个后缀树节点x上维护一个last[x]表示str[x]在当前串中最后一次出现的右端点,也就是right集合中的最大元素。
每当加入一个字符s[r]的时候parent树上会加入一个叶节点u,此时我们应该把根到u的路径上的last都赋值为r。。
再仔细思考一下。。。会发现根据原last(记作last’[x])和r可以更新答案。因为[last’[x]-maxlen[x]+1:last’[x]]和[r-maxlen[x]+1:r]是str[x]的最后两次出现。于是用一个线段树就可以维护答案了,注意还有长度在minlen[x]~maxlen[x]之间的子串也可以更新,所以要记录两个tag,一个记录len,一个记录pos。
链赋值对应着LCT的基本操作access。。于是我们用LCT来维护这个last。注意到同一棵splay里的last是一样的,我们只用在每棵splay的根节点记录last即可,这样修改就是 O(logn) O ( l o g n ) 的了,妙妙。
于是就能维护答案了
在线插入很简单。。因为答案是维护在线段树上的。。。把线段树可持久化就行了,第i棵线段树维护区间[1,i]即可。
#include<bits/stdc++.h>
#define pii pair<int,int>
#define mp make_pair
using namespace std;
int getint()
{
int i=0,f=1;char c;
for(c=getchar();c!='-'&&(c<'0'||c>'9');c=getchar());
if(c=='-')f=-1,c=getchar();
for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
return i*f;
}
const int N=200005,M=10000005;
int ol,ans,n,m,mxn;
int tot=1,last=1,son[N][26],fa[N],mx[N],r[N];
char s[N];
struct node{int lc,rc,mxl,mxp,tim;}tr[M];
int ttot,rt[N];
int modify(int y,int l,int r,int ql,int qr,int vl,int vp)
{
int x=tr[y].tim==n?y:++ttot;tr[x]=tr[y],tr[x].tim=n;
if(ql>qr)return x;
if(ql<=l&&r<=qr)
{
tr[x].mxl=max(tr[x].mxl,vl);
tr[x].mxp=max(tr[x].mxp,vp);
return x;
}
int mid=l+r>>1;
if(ql<=mid)tr[x].lc=modify(tr[y].lc,l,mid,ql,qr,vl,vp);
if(qr>mid)tr[x].rc=modify(tr[y].rc,mid+1,r,ql,qr,vl,vp);
return x;
}
pii query(int x,int l,int r,int p)
{
if(!x)return mp(-1,-1);
if(l==r)return mp(tr[x].mxl,tr[x].mxp);
int mid=l+r>>1;pii res;
res=p<=mid?query(tr[x].lc,l,mid,p):query(tr[x].rc,mid+1,r,p);
return mp(max(res.first,tr[x].mxl),max(res.second,tr[x].mxp));
}
struct LCT
{
int son[N][2],fa[N];
inline int which(int x){return son[fa[x]][1]==x;}
inline bool isrt(int x){return son[fa[x]][0]!=x&&son[fa[x]][1]!=x;}
inline void rotate(int x)
{
int y=fa[x],z=fa[y],t=which(x);
if(isrt(y))r[x]=r[y],r[y]=0;
else son[z][which(y)]=x;
fa[x]=z,fa[y]=x;
son[y][t]=son[x][t^1],son[x][t^1]=y;
if(son[y][t])fa[son[y][t]]=y;
}
inline void splay(int x)
{
while(!isrt(x))
{
if(!isrt(fa[x]))rotate(which(x)==which(fa[x])?fa[x]:x);
rotate(x);
}
}
inline void access(int x)
{
for(int y=0;x;y=x,x=fa[x])
{
splay(x);
if(son[x][1])r[son[x][1]]=r[x];
rt[n]=modify(rt[n],1,mxn,1,r[x]-mx[x],mx[x],-1);
rt[n]=modify(rt[n],1,mxn,max(1,r[x]-mx[x]+1),r[x],-1,r[x]);
son[x][1]=y;
if(!fa[x])r[x]=n;
}
}
}lct;
void extend(int c)
{
int p=last,np=last=++tot;mx[np]=mx[p]+1;
while(p&&!son[p][c])son[p][c]=np,p=fa[p];
if(!p)fa[np]=lct.fa[np]=1,lct.access(np);
else
{
int q=son[p][c];
if(mx[q]==mx[p]+1)fa[np]=lct.fa[np]=q,lct.access(np);
else
{
int nq=++tot;mx[nq]=mx[p]+1;memcpy(son[nq],son[q],sizeof(son[q]));
fa[nq]=lct.fa[nq]=fa[q],lct.splay(q),r[nq]=r[q],lct.access(nq);
fa[q]=lct.fa[q]=fa[np]=lct.fa[np]=nq,r[np]=n;
while(p&&son[p][c]==q)son[p][c]=nq,p=fa[p];
}
}
}
int main()
{
//freopen("lx.in","r",stdin);
ol=getint();scanf("%s",s+1),m=getint();
int len=strlen(s+1);mxn=len+m;
while(n<len)++n,rt[n]=rt[n-1],extend(s[n]-'a');
while(m--)
{
int op=getint(),l,r,c;
if(op==1)c=(getchar()-'a'+ans*ol)%26,++n,rt[n]=rt[n-1],extend(c);
else
{
int l=(getint()-1+ans*ol)%n+1,r=(getint()-1+ans*ol)%n+1;
pii res=query(rt[r],1,mxn,l);
ans=max(res.first,res.second-l+1);
printf("%d\n",ans);
}
}
return 0;
}