https://loj.ac/problem/6041
题意:给出一个01串,求出结束点在
的两个前缀的最长公共后缀长度
表示原字符串上位置,
表示在SAM上对应的点
求两个点的最长公共后缀长度,就是他们在后缀树上的LCA点的
先把询问离线下来,按右端点排序,从左向右顺着扫一遍,每次把当前点
到根节点的路径都打上
位置标记,如果新加入的点到根的路径上找到了标记,那么就要更新这个点的
对答案的贡献
到根节点的路径就是LCT的access操作,顺便找出打过标记的点
如图,当前右端点已经到了
在进行access操作时,找到了
节点,那么在线段树上
的mx值就可以更新成
,在线段树上查询区间
时,当
在
的右边时,这个
不会对答案贡献,当
在
的左边时,这个
可能对答案贡献。所以只需要在线段树上更新
的mx值
当结束完一个点的access操作和query后,再对它打上标记
#include <bits/stdc++.h>
#define ls (p << 1)
#define rs (p << 1 | 1)
#define lc ch[x][0]
#define rc ch[x][1]
using namespace std;
inline void read(int &x){
x = 0; int f = 1; char ch = getchar();
while (!(ch >= '0' && ch <= '9')){if (ch == '-') f = -1; ch = getchar();}
while (ch >= '0' && ch <= '9'){x = x * 10 + ch - '0'; ch = getchar();}
x *= f;
}
inline void Max(int &x, int y){if (y > x) x = y;}
const int N = 2e5 + 10;
int n, m;
char s[N];
int tot, pos[N];
struct Query{int l, r, id;} Q[N];
inline bool cmp(Query a, Query b){return a.r == b.r ? a.l < b.l : a.r < b.r;}
struct Seg{
int mx[N * 8];
inline void pushup(int p){mx[p] = max(mx[ls], mx[rs]);}
inline void update(int p, int ps, int v, int l = 1, int r = n){
if (l == r){Max(mx[p], v); return;}
int mid = (l + r) >> 1;
if (ps <= mid) update(ls, ps, v, l, mid);
else update(rs, ps, v, mid + 1, r);
pushup(p);
}
inline int query(int p, int ll, int rr, int l = 1, int r = n){
if (ll == l && rr == r) return mx[p];
int mid = (l + r) >> 1;
if (rr <= mid) return query(ls, ll, rr, l, mid);
else if (ll > mid) return query(rs, ll, rr, mid + 1, r);
else return max(query(ls, ll, mid, l, mid), query(rs, mid + 1, rr, mid + 1, r));
}
}seg;
struct SAM{
int ch[N][2], pre[N], mxl[N], last, cnt;
SAM(){last = cnt = 1;}
inline void Extend(int c, int i){
int p = last, np = ++cnt; last = np; pos[i] = np;
for (; p && !ch[p][c]; p = pre[p]) ch[p][c] = np;
if (!p){pre[np] = 1; return;}
int q = ch[p][c], nq = ++cnt;
if (mxl[q] == mxl[p] + 1){pre[np] = q; --cnt; return;}
memcpy(ch[nq], ch[q], sizeof(ch[q]));
mxl[nq] = mxl[p] + 1; pre[nq] = pre[q], pre[q] = pre[np] = nq;
for (; ch[p][c] == q; p = pre[p]) ch[p][c] = nq;
}
}sam;
struct LCT{
int ch[N][2], fa[N], pd[N], val[N];
inline bool chk(int x){return x == ch[fa[x]][1];}
inline bool nroot(int x){return x == ch[fa[x]][chk(x)];}
inline void pushdown(int x){
if (pd[x]){
if (lc) val[lc] = pd[lc] = pd[x];
if (rc) val[rc] = pd[rc] = pd[x];
pd[x] = 0;
}
}
inline void Rotate(int x){
int y = fa[x], z = fa[y], k = chk(x), w = ch[x][k ^ 1];
if (nroot(y)) ch[z][chk(y)] = x;
ch[y][k] = w, ch[x][k ^ 1] = y;
if (w) fa[w] = y;
fa[y] = x, fa[x] = z;
}
inline void pushall(int x){if (nroot(x)) pushall(fa[x]); pushdown(x);}
inline void splay(int x){
pushall(x);
while (nroot(x)){
int y = fa[x];
if (nroot(y)) Rotate((chk(x) ^ chk(y)) ? x : y);
Rotate(x);
}
}
inline void access(int x){
for (int y = 0; x; x = fa[y = x]){
splay(x);
if (val[x]) seg.update(1, val[x], sam.mxl[x]);
rc = y;
}
}
}lct;
int ans[N];
int main(){
read(n), read(m);
scanf("%s", s + 1);
for (int i = 1; i <= n; i++) sam.Extend(s[i] - '0', i);
for (int i = 1; i <= m; i++) read(Q[i].l), read(Q[i].r), Q[i].id = i;
sort(Q + 1, Q + 1 + m, cmp);
for (int i = 2; i <= sam.cnt; i++) lct.fa[i] = sam.pre[i];
for (int i = 1, j = 1; i <= n; i++){
lct.access(pos[i]);
while (j <= m && Q[j].r == i) ans[Q[j].id] = seg.query(1, Q[j].l, Q[j].r), ++j;
lct.splay(pos[i]), lct.val[pos[i]] = lct.pd[pos[i]] = i;
}
for (int i = 1; i <= m; i++) printf("%d\n", ans[i]);
return 0;
}