Problem
Solution
我周围的聚聚一看就说这不是道SA的傻逼题吗……
好吧,考虑用SAM怎么做。
先对反串建出SAM,[a,b]中的所有子串不好处理,因为在parent数上代表的链实在太多了,所以考虑从子串[c,d]开始下手
我们可以考虑二分lcp的长度,那么我们可以在
O
(
log
n
)
O(\log n)
O(logn)的时间内找到这个子串所对应的节点,然后我们可以用主席树来查在[a,b]中是否有存在这么一个子串。
如何在 O ( log n ) O(\log n) O(logn)的时间内找到这个子串所对应的节点?我们在建SAM时可以记录前缀 [ 1 , i ] [1,i] [1,i]所代表的节点,然后在parent树找到最深的节点使得longest大于等于子串长,倍增即可。
时间复杂度 O ( m log 2 n ) O(m\log^2 n) O(mlog2n)
Code
#include <assert.h>
#include <algorithm>
#include <cstring>
#include <cstdio>
#define rg register
using namespace std;
typedef long long ll;
const int maxn=200010,maxm=4000010;
template <typename Tp> inline int getmin(Tp &x,Tp y){return y<x?x=y,1:0;}
template <typename Tp> inline int getmax(Tp &x,Tp y){return y>x?x=y,1:0;}
template <typename Tp> inline void read(Tp &x)
{
x=0;int f=0;char ch=getchar();
while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
if(ch=='-') f=1,ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
if(f) x=-x;
}
int n,m,sz,lst,cnt,ans,ch[maxn][26],l[maxn],pre[maxn],pos[maxn];
int t[maxn],top[maxn],f[18][maxn],rt[maxn],lc[maxm],rc[maxm];
char s[maxn];
void insert(int c)
{
int p=lst,np=++sz;
l[np]=l[p]+1;lst=np;
for(;!ch[p][c]&&p;p=pre[p]) ch[p][c]=np;
if(!p) pre[np]=1;
else
{
int q=ch[p][c];
if(l[q]==l[p]+1) pre[np]=q;
else
{
int nq=++sz;l[nq]=l[p]+1;
memmove(ch[nq],ch[q],sizeof(ch[q]));
pre[nq]=pre[q];pre[np]=pre[q]=nq;
for(;ch[p][c]==q;p=pre[p]) ch[p][c]=nq;
}
}
}
int getpos(int ql,int qr)
{
int len=qr-ql+1,x=pos[qr];
for(int i=17;~i;i--)
if(l[f[i][x]]>=len)
x=f[i][x];
return x;
}
inline int new_node(){if(cnt+10<maxm) return ++cnt;assert(0);}
void update(int l,int r,int pos,int& rt)
{
if(!rt) rt=new_node();
if(l==r) return ;
int m=(l+r)>>1;
if(pos<=m) update(l,m,pos,lc[rt]);
else update(m+1,r,pos,rc[rt]);
}
int query(int l,int r,int L,int R,int rt)
{
if(!rt) return 0;
if(L<=l&&r<=R) return 1;
int m=(l+r)>>1,res=0;
if(L<=m) res|=query(l,m,L,R,lc[rt]);
if(res) return 1;
if(m<R) res|=query(m+1,r,L,R,rc[rt]);
return res;
}
int merge(int x,int y)
{
if(!x||!y) return x+y;
int now=new_node();
lc[now]=merge(lc[x],lc[y]);
rc[now]=merge(rc[x],rc[y]);
return now;
}
void input()
{
read(n);read(m);
scanf("%s",s+1);
reverse(s+1,s+n+1);
lst=sz=1;
for(rg int i=1;i<=n;i++)
{
insert(s[i]-'a');pos[i]=lst;
update(1,n,i,rt[lst]);
}
for(rg int i=1;i<=sz;i++) ++t[l[i]],f[0][i]=pre[i];
for(rg int i=1;i<=sz;i++) t[i]+=t[i-1];
for(rg int i=1;i<=sz;i++) top[t[l[i]]--]=i;
for(rg int i=1;i<=sz;i++)
{
int x=top[i];
for(rg int j=1;j<18;j++)
f[j][x]=f[j-1][f[j-1][x]];
}
for(rg int i=sz;i>1;i--)
{
int x=top[i];
rt[pre[x]]=merge(rt[pre[x]],rt[x]);
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
#endif
int a,b,c,d,x,L,R,mid;
input();
while(m--)
{
read(a);read(b);read(c);read(d);
a=n-a+1;b=n-b+1;c=n-c+1;d=n-d+1;
L=1;R=c-d+1;ans=0;
while(L<=R)
{
mid=(L+R)>>1;
x=getpos(c-mid+1,c);
if(b+mid-1<=a&&query(1,n,b+mid-1,a,rt[x])) ans=mid,L=mid+1;
else R=mid-1;
}
printf("%d\n",ans);
}
return 0;
}