P4094 [HEOI2016/TJOI2016] 字符串
非常好题目,使我的 SAM 旋转。
前置芝士:
-
SAM 后缀自动机
-
线段树合并(动态开点与权值线段树)
形式化题意:
给出字符串 $s$,每次询问 $a,b,c,d$。求 $s[a...b]$ 的所有子串和 $s[c...d]$ 的最长公共前缀的长度的最大值。
一个做法的乱讲:
注意到我们当然可以构造 SAM,但是暴力的比对和查找只会荣获一个 $n^3$的若智做法(和暴力没差)。注意到我们可以反过来看题目,干脆求出 $s[c...d]$ 在 $s[a...b]$ 中出现的最大长度,这样就完成了题意转化,我们从这个入手。
我们假设现在已经有了 $s[c...e]$ (e ∈[c,d]) 在 $s[a...b]$ 中出现过,那么显然 $s[c...e]$ 的所有前缀都是合法的前缀,相同的我们也可以很容易得到若 $s[c...e]$ (e ∈[c,d]) 在 $s[a...b]$ 中没出现过,那么 $s[c...f]$ (f > e) 当然也不可能出现,于是我们可以二分答案。
考虑如何 check,回忆起 SAM 中 $endpos$ 的定义,我们可以维护 $endpos (s[c...e])$,查询 $s[(a + e - c)...b]$ 的子串集合在 $endpos (s[c...e])$ 中是否有元素。至于维护,考虑在 SAM 上对代表这些子串的 id 建立权值线段树(因为只考虑“存在性”所以存布尔就行),用线段树合并的方式在总复杂度为 $O(n \log n)$ 的时间内处理所有区间。直接将 $[a + e - c, b]$ 扔到 $endpos(s[c...e]) $上查就行。
问题来到了如何找出代表着 $s[c...e]$ 的 SAM 上的 $id$,注意到在 parent tree 中,我们可以通过不断从 $s[1...e]$ 的位置往上跳来达到“遇见”$s[c...e]$ 的位置,至于判断遇见的是不是 $s[c...e]$,考虑在 parent tree 上单纯的从下往跑,$len$ 也下降,我们可以用这一点来判定。类似于求 lca,这一操作也可以用倍增优化。优化后达到优美的 $O( \log n)$ 的单次复杂度。
非常错误的复杂度分析:
预处理 $n \log n$ + 询问 $n$* 二分 $\log n$ * (求 $p$ $\log n$ + 查询 $\log n$) = $O(n \log ^2 n)$
代码
#include <bits/extc++.h>
#define e_ putchar_unlocked(' ')
#define en_ putchar_unlocked('\n')
using lint = long long;
using namespace std;
template <typename T> inline T in(T &n) {
n = 0; char p = getchar_unlocked();
while(p < '-') p = getchar_unlocked();
// bool f = p == '-' ? (p = getchar_unlocked()) : 0;//
do n = (n << 1) + (n << 3) + (p ^ 48), p = getchar_unlocked();
while(isdigit(p));
// return n = f ? -n : n;//
return n;
}
template <typename T> inline void out(T x) {
// if(x < 0) putchar_unlocked('-'), x = -x; //
if(x >= 10) out(x / 10);
putchar_unlocked(x % 10 + '0');
}
const int N = 1e5 + 10;
namespace DSU{
bool w[N * 100];
int ls[N * 100], rs[N * 100];
int cntn;
inline void up(int u) {
w[u] = w[ls[u]] | w[rs[u]];
}
inline void update(int &u, int l, int r, int p) {
if(!u) u = ++cntn;
if(l == r) { w[u] = 1; return;}
int m = (l + r) >> 1;
if(m >= p) update(ls[u], l, m, p);
else update(rs[u], m + 1, r, p);
up(u);
}
inline bool query(int u, int l, int r, int L, int R) {
if(!u) return 0;
if(L <= l and r <= R) return w[u];
if(L > r || l > R) return 0;
int m = (l + r) >> 1;
return query(ls[u], l, m, L, R) | query(rs[u], m + 1, r, L, R);
}
inline int merge(int u, int v, int l, int r) {
if(!u || !v) return u | v;
int now = ++cntn;
if(l == r) { w[now] = w[v] | w[u]; return now;}
int m = (l + r) >> 1;
ls[now] = merge(ls[u], ls[v], l, m);
rs[now] = merge(rs[u], rs[v], m + 1, r);
up(now); return now;
}
}
using namespace DSU;
int rev[N], rt[N * 2];
int n, q;
string s;
namespace SAM {
#define link LINK
int ch[N * 2][26], link[N * 2], len[N * 2], tot = 1, las = 1;
vector<int> e[N * 2];
inline void add(int c) {
int p = las, np = las = ++ tot;
len[np] = len[p] + 1,
update(rt[np], 1, n, len[np]);
for(; p and !ch[p][c]; p = link[p]) ch[p][c] = np;
if(!p) link[np] = 1;
else {
int q = ch[p][c];
if(len[q] == len[p] + 1) link[np] = q;
else {
int nq = ++ tot;
memcpy(ch[nq], ch[q], sizeof ch[0]);
link[nq] = link[q];
len[nq] = len[p] + 1;
link[q] = link[np] = nq;
for(; p and ch[p][c] == q; p = link[p]) ch[p][c] = nq;
}
}
}
int fa[N * 2][20];
inline void dfs(int u) {
for(int i = 1; i <= 18; i ++)
fa[u][i] = fa[fa[u][i - 1]][i - 1];
for(int v : e[u]) {
dfs(v);
rt[u] = merge(rt[u], rt[v], 1, n);
}
}
inline void init() {
for(int i = 0; i < n; i ++) add(s[i] - 'a');
for(int i = 2; i <= tot; i ++) fa[i][0] = link[i], e[link[i]].push_back(i);
dfs(1);
}
}
using namespace SAM;
inline bool check(int a, int b, int c, int mid) {
int p = rev[c + mid - 1];
for(int i = 18; i >= 0; i --) {
if(fa[p][i] and len[fa[p][i]] >= mid) p = fa[p][i];
}
return query(rt[p], 1, n, a + mid - 1, b);
}
signed main() {
#ifndef ONLINE_JUDGE
freopen("in.in", "r", stdin);
#endif
in(n), in(q);
cin >> s;
init();
for(int i = 0, p = 1; i < n; i ++) {
p = ch[p][s[i] - 'a'],
rev[i + 1] = p;
}
for(int i = 1, a, b, c, d; i <= q; i ++) {
in(a), in(b), in(c), in(d);
int l = 0, r = min(b - a + 1, d - c + 1), mid;
while(l < r) {
mid = (l + r + 1) >> 1;
if(check(a, b, c, mid)) l = mid;
else r = mid - 1;
}
out(r), en_;
}
}

浙公网安备 33010602011771号