Link
先考虑离线做法:动态构建SAM,同时维护(f_l)表示以当前串末尾为右端点,左端点为(l)的答案。
插入一个字符会改变它在parent树上祖先的(endpos)集合,考虑往(endpos)集合中插入一个元素的贡献。
对于(xsubseteq endpos),左端点为(x-i+1)的答案要和(i)取(max)。
由此增加的贡献是一个等差数列,因此可以线段树维护。
贡献相同的区间显然越短越好,因此只要考虑插入元素和集合最大的元素一起产生的的贡献。
那么每个节点维护(endpos)集合最大元素,因为每次更新一个点和它的所有的祖先,且更新的链上的(endpos)集合最大元素相同,用LCT维护即可。
在线的话只需要把线段树改成主席树就可以了。
#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
const int N=200007;
int read(){int x=0,c=getchar();while(!isdigit(c))c=getchar();while(isdigit(c))x=x*10+c-48,c=getchar();return x;}
int fetch(){char c=getchar();while(!islower(c))c=getchar();return c;}
int n,cnt=1,tot,now=1,root1[N],root2[N],ch[N][2],fa[N],tag[N],len[N],link[N],trans[N][26];
char str[N];
struct node{int ls,rs,mx;}t[N*200];
#define mid ((l+r)>>1)
void update(int&p,int l,int r,int L,int R,int x)
{
t[++tot]=t[p],p=tot;
if(L<=l&&r<=R) return t[p].mx=std::max(t[p].mx,x),void();
if(L<=mid) update(t[p].ls,l,mid,L,R,x);
if(R>mid) update(t[p].rs,mid+1,r,L,R,x);
}
int query(int p,int l,int r,int x){return l==r? t[p].mx:std::max(t[p].mx,x<=mid? query(t[p].ls,l,mid,x):query(t[p].rs,mid+1,r,x));}
#undef mid
#define lc ch[x][0]
#define rc ch[x][1]
int nroot(int x){return ch[fa[x]][0]==x||ch[fa[x]][1]==x;}
void pushall(int x){if(!x)return;if(nroot(x))pushall(fa[x]);tag[lc]=tag[rc]=tag[x];}
void rotate(int x)
{
int y=fa[x],z=fa[y],k=ch[y][1]==x;
if(nroot(y)) ch[z][y==ch[z][1]]=x;
fa[x]=z,fa[y]=x,fa[ch[x][!k]]=y,ch[y][k]=ch[x][!k],ch[x][!k]=y;
}
void splay(int x)
{
pushall(x);
for(int y;nroot(x);rotate(x)) if(nroot(y=fa[x])) rotate((ch[y][0]==x)^(ch[fa[y]][0]==y)? x:y);
}
void access(int x)
{
for(int y=0;x;x=fa[y=x])
if(splay(x),(rc=y)&&tag[x]&&len[x])
{
if(tag[x]>len[x]) update(root1[n],1,100000,1,tag[x]-len[x],len[x]);
if(tag[x]>=len[x]) update(root2[n],1,100000,tag[x]-len[x]+1,tag[x],tag[x]);
}
}
void cut(int x,int y){splay(x),splay(y),ch[y][1]^x? splay(x),fa[x]=0:ch[y][1]=fa[x]=0;}
void make(int x){access(x),splay(x),tag[x]=n;}
#undef lc
#undef rc
void extend(int c)
{
int p=now,q;len[now=++cnt]=len[p]+1;
for(;p&&!trans[p][c];p=link[p]) trans[p][c]=now;
if(!p) return fa[now]=link[now]=1,make(now);
if(len[q=trans[p][c]]==len[p]+1) return fa[now]=link[now]=q,make(now);
memcpy(trans[++cnt],trans[q],104),len[cnt]=len[p]+1,splay(q),tag[cnt]=tag[q],cut(q,link[q]),fa[cnt]=link[cnt]=link[q],fa[q]=link[q]=fa[now]=link[now]=cnt,make(now);
for(;p&&trans[p][c]==q;p=link[p]) trans[p][c]=cnt;
}
int main()
{
scanf("%s",str+1);
for(int i=1;str[i];++i) ++n,root1[n]=root1[n-1],root2[n]=root2[n-1],extend(str[i]-'a');
for(int m=read(),ans=0;m;--m)
if(read()==1) str[0]=(fetch()-'a'+ans)%26+'a',str[++n]=str[0],root1[n]=root1[n-1],root2[n]=root2[n-1],extend(str[0]-'a');
else
{
int l=(read()+ans-1)%n+1,r=(read()+ans-1)%n+1;
if(l>r) std::swap(l,r);
printf("%d
",ans=std::max(query(root1[r],1,100000,l),query(root2[r],1,100000,l)-l+1));
}
}