题目
题目描述
人的一生不仅要靠自我奋斗,还要考虑到历史的行程。
历史的行程可以抽象成一个 01 串,作为一个年纪比较大的人,你希望从历史的行程中获得一些姿势。
你发现在历史的不同时刻,不断的有相同的事情发生。比如,有两个人同时在世纪之交 11 年的时候上台,同样喜欢与洋人谈笑风生,同样提出了以「三」字开头的理论。
你发现,一件事情可以看成是这个 01 串的一个前缀,这个前缀最右边的位置就是这个事情的结束时间。
两件事情的相似度可以看成,这两个前缀的最长公共后缀长度。
现在你很好奇,在一段区间内结束的事情中最相似的两件事情的相似度是多少呢?
输入格式
第一行两个整数 $n$、$m$,表示串长和询问个数。 第二行长度为 $n$ 的 01 串,表示历史的行程。 接下来 $m$ 行,每行两个正整数 $l$、$r$ 表示询问的区间,包括端点,保证 $1 \leq l < r \leq n$。
输出格式
输出 $m$ 行,对每个询问输出一个整数表示最大的相似度。
样例
样例输入 1
4 2
0101
1 3
2 4
样例输出 1
1
2
数据范围与提示
测试点 | $n =$ | $m =$ |
---|---|---|
1 ~ 2 | $100$ | $100$ |
3 ~ 4 | $300$ | $1000$ |
5 ~ 6 | $5000$ | $5000$ |
7 ~ 10 | $100000$ | $300$ |
11 ~ 14 | $50000$ | $50000$ |
15 ~ 20 | $100000$ | $100000$ |
题解
因为两个状态的$lcs$(最长公共后缀Longest Common Suffix)就是ta们在后缀树上$lca$的$len$,所以题目就是要求一个区间内两点的$len_{lca}$最大是多少
对于某个点,能让ta作为答案的只有ta的不同子树里的点或者ta自己和子树中的某个点
所以可以启发式合并,记录一下当前枚举点在原序列中最接近的点,记录下来,这个用set
可以维护了
把询问离线,按右端点排序,然后扫描线即可
代码
# include<iostream>
# include<cstring>
# include<cstdio>
# include<set>
# include<algorithm>
# define tl (k<<1)
# define tr (k<<1|1)
# define mid (l+r>>1)
using namespace std;
const int MAX=2e5+5;
struct p{
int x,y,L;
bool operator< (const p &a)
const{
return y<a.y;
}
}b[MAX<<4];
struct q{
int l,r,id;
bool operator< (const q &a)
const{
return r<a.r;
}
}qu[MAX];
struct o{
int x;
}s[MAX<<1];
int n,cnt,Q;
int ans[MAX];
char a[MAX];
struct SAM{
struct u{
int x,y;
}c[MAX];
int l,r,L,num;
int h[MAX],len[MAX],fa[MAX];
int son[MAX][2];
set<int> s[MAX];
SAM() {l=r=1;}
void add(int x,int y) {c[++num]=(u){h[x],y},h[x]=num;}
void ins(int x,int id)
{
int tt=r;
len[r=++l]=++L,s[r].insert(id);
for(;tt&&!son[tt][x];tt=fa[tt])
son[tt][x]=r;
if(!tt) return void(fa[r]=1);
int qwq=son[tt][x];
if(len[qwq]==len[tt]+1) return void(fa[r]=qwq);
len[++l]=len[tt]+1,fa[l]=fa[qwq],fa[qwq]=fa[r]=l,son[l][0]=son[qwq][0],son[l][1]=son[qwq][1];
for(int i=tt;son[i][x]==qwq;i=fa[i])
son[i][x]=l;
}
void dfs(int x=1)
{
for(int i=h[x];i;i=c[i].x)
{
dfs(c[i].y);
if(!len[x]) continue;
if(s[x].size()<s[c[i].y].size()) swap(s[x],s[c[i].y]);
for(set<int>::iterator A=s[c[i].y].begin(),B;A!=s[c[i].y].end();++A)
{
B=s[x].upper_bound(*A);
if(B!=s[x].end()) b[++cnt]=(p){*A,*B,len[x]};
if(B!=s[x].begin()) b[++cnt]=(p){*(--B),*A,len[x]};
}
for(set<int>::iterator A=s[c[i].y].begin();A!=s[c[i].y].end();++A)
s[x].insert(*A);
}
}
void Init()
{
for(int i=2;i<=l;++i)
add(fa[i],i);
dfs(),sort(b+1,b+1+cnt);
}
}_S;
int read()
{
int x(0);
char ch=getchar();
for(;!isdigit(ch);ch=getchar());
for(;isdigit(ch);x=x*10+ch-48,ch=getchar());
return x;
}
void change(int l,int r,int k,int x,int dis)
{
s[k].x=max(s[k].x,dis);
if(l==r) return;
if(x<=mid) change(l,mid,tl,x,dis);
else change(mid+1,r,tr,x,dis);
}
int ask(int l,int r,int k,int L,int R)
{
if(r<L||R<l) return 0;
if(l>=L&&r<=R) return s[k].x;
return max(ask(l,mid,tl,L,R),ask(mid+1,r,tr,L,R));
}
int main()
{
n=read(),Q=read(),scanf("%s",a+1);
for(int i=1;i<=n;++i)
_S.ins(a[i]-'0',i);
_S.Init();
for(int i=1;i<=Q;++i)
qu[i].l=read(),qu[i].r=read(),qu[i].id=i;
sort(qu+1,qu+1+Q);
for(int i=1,L=1;i<=Q;++i)
{
while(b[L].y<=qu[i].r&&L<=cnt) change(1,n,1,b[L].x,b[L].L),++L;
ans[qu[i].id]=ask(1,n,1,qu[i].l,qu[i].r);
}
for(int i=1;i<=Q;++i)
printf("%d\n",ans[i]);
return 0;
}