題意:給一個字符串,求區間
題解:
對於一個串 ,求的就是
這啓示我們從 開始跳 樹,到一個點的 的 就是當前的
然後用線段樹合併維護 集合
在 集合中找一個滿足 的最大的
顯然一步一步跳不行,考慮樹剖,一次跳一個鏈,對答案有貢獻的是鏈的一個前綴
離線,鏈分治,將一個詢問拆成 個,再對每一條重鏈單獨處理
由於有貢獻的是一個前綴,我們動態從上向下做
,從上到下做的時候暴力將輕兒子的所有點插入某一種數據結構
來看看這種數據結構需要幹什麼
就是在一個區間 找一個滿足 的最大的
線段樹上二分,維護 的最小值
這樣一來就解決了重鏈上一個前綴的所有輕兒子的貢獻
還差一個鏈上的點的重兒子的貢獻
對於這個重兒子的貢獻,我們用最開始的線段樹合併的方法就可以解決
複雜度
一句話題解:
將問題轉換爲跳 樹,進一步轉換爲跳重鏈,鏈分治對每一條重鏈分別處理,處理的時候 暴力處理輕兒子,線段樹合併處理重兒子
感覺鏈分治的思想蠻巧妙的!
#include<bits/stdc++.h>
#define cs const
using namespace std;
int read(){
int cnt = 0, f = 1; char ch = 0;
while(!isdigit(ch)){ ch=getchar(); if(ch=='-')f=-1; }
while(isdigit(ch)) cnt=cnt*10+(ch-'0'),ch=getchar();
return cnt * f;
}
cs int N = 4e5 + 5;
char s[N];
int n,m,ql[N],qr[N],ans[N];
int ps[N],bin[N];
namespace SAM{
int ch[N][26],lk[N],len[N],nd;
void init(){ nd=n+1; for(int i=1; i<=n; i++) len[i]=i; }
void extend(int i, int c){
int now=i,p=(i-1)?(i-1):n+1;
for(;p&&!ch[p][c];p=lk[p]) ch[p][c]=now;
if(!p) lk[now]=n+1;
else{
int q=ch[p][c];
if(len[q]==len[p]+1) lk[now]=q;
else{
int cl=++nd; len[cl]=len[p]+1; lk[cl]=lk[q];
memcpy(ch[cl],ch[q],sizeof(ch[q]));
lk[now]=lk[q]=cl;
for(;p&&ch[p][c]==q;p=lk[p]) ch[p][c]=cl;
}
}
}
void radix_sort(){
for(int i=1; i<=nd; i++) ++bin[len[i]];
for(int i=1; i<=n; i++) bin[i]+=bin[i-1];
for(int i=nd; i>=1; i--) ps[bin[len[i]]--]=i;
}
}
int son[N],top[N],sz[N],fa[N];
vector<int>G[N];
void Div(){
for(int i=SAM::nd; i>=1; i--){
int u=ps[i]; fa[u]=SAM::lk[u];
sz[fa[u]]+=++sz[u];
if(sz[son[fa[u]]]<sz[u]) son[fa[u]]=u;
G[fa[u]].push_back(u);
}
for(int i=1; i<=SAM::nd; i++){
int u=ps[i]; if(!top[u]) top[u]=u;
if(son[u]) top[son[u]]=top[u];
}
}
vector<int>v[N];
void addqry(int i,int u){
if(ql[i]==qr[i]) return;
while(u) v[u].push_back(i),u=fa[top[u]];
}
namespace SGT1{
cs int M=N*40;
int rt[N],mx[M],ls[M],rs[M],nd;
#define mid ((l+r)>>1)
void ins(int &x, int l, int r, int p){
if(!x) x=++nd; mx[x]=max(mx[x],p); if(l==r) return;
(p<=mid)?ins(ls[x],l,mid,p):ins(rs[x],mid+1,r,p);
}
int merge(int x, int y){
if(!x||!y) return x|y;
ls[x]=merge(ls[x],ls[y]);
rs[x]=merge(rs[x],rs[y]);
mx[x]=max(mx[ls[x]],mx[rs[x]]); return x;
}
int query(int x, int l, int r, int L, int R){
if(!x) return 0; if(L<=l&&r<=R) return mx[x]; int ans=0;
if(L<=mid) ans=max(ans,query(ls[x],l,mid,L,R));
if(R>mid) ans=max(ans,query(rs[x],mid+1,r,L,R)); return ans;
}
void Main(){
for(int i=1; i<=n; i++) ins(rt[i],1,n,i);
for(int i=SAM::nd;i>=1;i--){
int u=ps[i];
for(int id:v[u])
ans[id]=max(ans[id],query(rt[u],1,n,ql[id],min(ql[id]+SAM::len[u]-1,qr[id]-1))-ql[id]+1);
rt[fa[u]]=merge(rt[fa[u]],rt[u]);
}
}
#undef mid
}
namespace SGT2{
cs int N=::N<<2;
int ls[N],rs[N],mi[N],rt,nd;
#define mid ((l+r)>>1)
void ins(int &x, int l, int r, int p, int v){
if(!x){ x=++nd; ls[x]=rs[x]=0; mi[x]=1e9; }
mi[x]=min(mi[x],v); if(l==r) return;
(p<=mid)?ins(ls[x],l,mid,p,v):ins(rs[x],mid+1,r,p,v);
}
int query(int x, int l, int r, int L, int R, int v){
if(!x||mi[x]>v) return 0; if(l==r) return l;
if(R<=mid) return query(ls[x],l,mid,L,R,v);
if(L>mid) return query(rs[x],mid+1,r,L,R,v);
int ans=0;
if(rs[x]&&mi[rs[x]]<=v) ans=query(rs[x],mid+1,r,L,R,v);
if(ans) return ans;
return query(ls[x],l,mid,L,R,v);
}
void dsu(int u, int len){
if(u<=n) ins(rt,1,n,u,u-len+1);
for(int t:G[u]) dsu(t,len);
}
void dfs(int u){
for(int p=u;p;p=son[p])
for(int t:G[p]) if(t^son[p]) dfs(t);
rt=nd=0;
for(int p=u;p;p=son[p]){
for(int t:G[p]) if(t^son[p]) dsu(t,SAM::len[p]);
if(p<=n) ins(rt,1,n,p,p-SAM::len[p]+1);
for(int id:v[p])
ans[id]=max(ans[id],query(rt,1,n,ql[id],qr[id]-1,ql[id])-ql[id]+1);
}
}
}
int main(){
scanf("%s",s+1); n=strlen(s+1); SAM::init();
for(int i=1; i<=n; i++) SAM::extend(i,s[i]-'a');
SAM::radix_sort(); Div();
m=read();
for(int i=1; i<=m; i++){
ql[i]=read(),qr[i]=read();
addqry(i,qr[i]);
}
SGT1::Main();
SGT2::dfs(n+1);
for(int i=1; i<=m; i++) cout<<ans[i]<<'\n';
return 0;
}