题解 P4094 【[HEOI2016/TJOI2016]字符串】
SAM
怎么题解都没有几篇SAM的,SAM会伤心的。
Part.1
首先观察询问,好像非常不可做,但是可以发现答案有可二分性,具体来说,可以二分最长公共前缀的长度,然后就是询问
如果对SAM敏感的就可以发现这其实就可以发现这是一个在SAM上线段树合并预处理然后倍增跳
于是就完了(不是
Part.2
具体来说,我们知道一个
每次假设我们钦定了右端点为
做完了可以去做一下这个,跟这个比较像。
const int maxn=1e5+5;
struct SegmentTree
{
int lc[maxn*40],rc[maxn*40],wife[maxn*40],cnt;
void modify(int &u,int l,int r,int x)
{
if(!u) u=++cnt;
++wife[u];
if(l==r) return;
int mid=(l+r)>>1;
if(x<=mid) modify(lc[u],l,mid,x);
else modify(rc[u],mid+1,r,x);
}
int merge(int u,int v)
{
if(!u||!v) return u|v;
int x=++cnt;
wife[x]=wife[u]+wife[v];
lc[x]=merge(lc[u],lc[v]);
rc[x]=merge(rc[u],rc[v]);
return x;
}
int query(int u,int l,int r,int x,int y)
{
if(!u) return 0;
if(x<=l&&r<=y) return wife[u];
int mid=(l+r)>>1,res=0;
if(x<=mid) res+=query(lc[u],l,mid,x,y);
if(y>mid) res+=query(rc[u],mid+1,r,x,y);
return res;
}
}wife;
int tr[maxn<<1][26],maxlen[maxn<<1],link[maxn<<1],cnt=1,las=1,tax[maxn<<1],a[maxn<<1];
int fa[maxn<<1][21],n,pos[maxn],rt[maxn<<1];
char s[maxn];
void insert(int c)
{
int u=las,nu=las=++cnt;
maxlen[nu]=maxlen[u]+1;
for(;u&&!tr[u][c];u=link[u]) tr[u][c]=nu;
if(!u) return link[nu]=1,void();
int v=tr[u][c];
if(maxlen[u]+1==maxlen[v]) return link[nu]=v,void();
int nv=++cnt;
maxlen[nv]=maxlen[u]+1;link[nv]=link[v];link[v]=link[nu]=nv;
memcpy(tr[nv],tr[v],sizeof(tr[v]));
for(;u&&tr[u][c]==v;u=link[u]) tr[u][c]=nv;
}
inline bool check(int lim,int u,int l,int r)
{
for(int i=20;i>=0;--i) if(fa[u][i]&&maxlen[fa[u][i]]>=lim) u=fa[u][i];
return wife.query(rt[u],1,n,l+lim-1,r);
}
int main()
{
int q,x,y,c,d;
scanf("%d%d%s",&n,&q,s+1);
reverse(s+1,s+n+1);
for(int i=1;i<=n;++i) insert(s[i]-'a'),pos[i]=las,wife.modify(rt[las],1,n,i);
for(int i=1;i<=cnt;++i) ++tax[maxlen[i]],fa[i][0]=link[i];
for(int i=1;i<=cnt;++i) tax[i]+=tax[i-1];
for(int i=cnt;i;--i) a[tax[maxlen[i]]--]=i;
for(int i=cnt;i>1;--i) rt[link[a[i]]]=wife.merge(rt[a[i]],rt[link[a[i]]]);
for(int j=1;j<=20;++j)
for(int i=1;i<=cnt;++i)
fa[i][j]=fa[fa[i][j-1]][j-1];
while(q--)
{
scanf("%d%d%d%d",&x,&y,&c,&d);
x=n-x+1;y=n-y+1;c=n-c+1;d=n-d+1;
int l=0,r=min(x-y+1,c-d+1),mid,ans=0;
while(l<=r)
{
int mid=(l+r)>>1;
if(check(mid,pos[c],y,x)) l=mid+1,ans=mid;
else r=mid-1;
}
printf("%d\n",ans);
}
return 0,QAQ;
}