Statement
先把串 reverse,多次给 \(l,r\),求
\[ \max_{l\le i<j\le r}\{\text{LCP}(i,j)\}
\]
Solution
- \(\text{sqrtlog}\sim\text{sqrt}\):莫队 + 线段树 / 树状数组 / set,用 SA 做
- \(nm/\omega\):bitset 乱搞
- \(\log^2\):SAM + LCT + BIT
在 parent 树上,LCP 等于 LCA 的 len
离线,每次加入右端点就 access 一下,过程中对产生的 LCA 关系进行更新.
- 还有一种做法是在 parent 树上 set 启发式合并,然后二维数点
Code
一遍写完一遍过!太牛了。
#include <bits/stdc++.h>
using namespace std;
#define rep(i, j, k) for (int i = (j); i <= (k); ++i)
#define reo(i, j, k) for (int i = (j); i >= (k); --i)
typedef long long ll;
const int N = 2e5 + 10;
vector<pair<int, int>> Queries[N];
int n, m;
string s;struct BIT {int len, mx[N];BIT(int _len = 0) {len = _len, memset(mx, 0, sizeof(mx));}void Upd(int x, int v) {for (; x <= len; x += x & -x) mx[x] = max(mx[x], v);}int Qry(int x) {int res = 0;for (; x; x -= x & -x) res = max(res, mx[x]);return res;}
} bit;namespace SAM {int sz, cur, last, len[N], link[N], nxt[N][2];void init() {link[0] = -1;}void extend(int ch) {cur = ++sz, len[cur] = len[last] + 1;int p = last;for (; ~p; p = link[p])if (!nxt[p][ch]) nxt[p][ch] = cur;else break;if (!~p) {link[cur] = 0;} else {int q = nxt[p][ch];if (len[p] + 1 == len[q]) {link[cur] = q;} else {int copy = ++sz;link[copy] = link[q], link[q] = link[cur] = copy, len[copy] = len[p] + 1;nxt[copy][0] = nxt[q][0], nxt[copy][1] = nxt[q][1];for (; ~p; p = link[p])if (nxt[p][ch] == q) nxt[p][ch] = copy;else break;}}last = cur;}void Build() {init();for (auto ch : s) extend(ch - 48);bit = BIT(n);}
}int now;namespace LinkCutTree {int fa[N], ch[N][2], tag[N], val[N];#define get(u) (u == ch[fa[u]][1])#define nrt(u) (u == ch[fa[u]][0] || u == ch[fa[u]][1])void cov(int u, int v) {if (u) tag[u] = val[u] = v;}void down(int u) {if (tag[u]) cov(ch[u][0], tag[u]), cov(ch[u][1], tag[u]), tag[u] = 0;}void Down(int u) {if (nrt(u)) Down(fa[u]);down(u);}void rot(int u) {int f = fa[u], g = fa[f], k = get(u);if (nrt(f)) ch[g][get(f)] = u;ch[f][k] = ch[u][!k];if (ch[u][!k]) fa[ch[u][!k]] = f;ch[u][!k] = f, fa[f] = u, fa[u] = g;}void splay(int u) {for (Down(u); nrt(u); rot(u)) if (nrt(fa[u])) rot(get(u) == get(fa[u]) ? fa[u] : u);}void access(int u) {int v = 0;for (; u; v = u, u = fa[u]) {splay(u);if (v) {bit.Upd(n - val[u] + 1, SAM::len[u - 1]);}ch[u][1] = v;}cov(v, now);}#undef get#undef nrt
}
int ans[N];int main() {ios::sync_with_stdio(false), cin.tie(nullptr);cin >> n >> m >> s;rep(i, 1, m) {int l, r;cin >> l >> r;Queries[r].push_back(make_pair(l, i));}SAM::Build();rep(i, 1, SAM::sz) LinkCutTree::fa[i + 1] = SAM::link[i] + 1;int pos = 0;rep(i, 1, n) {now = i;pos = SAM::nxt[pos][s[i - 1] - 48];LinkCutTree::access(pos + 1);for (auto qry : Queries[i])ans[qry.second] = bit.Qry(n - qry.first + 1);}rep(i, 1, m) {cout << ans[i] << '\n';}return 0;
}