Address
Solution
先二分答案,转化成判定性问题:
s
[
a
.
.
.
b
]
s[a...b]
s[a...b] 是否存在一个子串和
s
[
c
.
.
.
d
]
s[c...d]
s[c...d] 的 lcp 大于等于
m
i
d
mid
mid 。
等价地,判断是否存在一个
i
∈
[
a
,
b
−
m
i
d
+
1
]
i\in[a,b-mid+1]
i∈[a,b−mid+1] 满足以
i
i
i 为开头的后缀和以
c
c
c 开头的后缀的 lcp 大于等于
m
i
d
mid
mid 。
将
s
s
s 的后缀数组构建出来之后, lcp 可以转化成
h
e
i
g
h
t
height
height 区间最小值。
所以不管
r
a
n
k
[
i
]
<
r
a
n
k
[
c
]
rank[i]<rank[c]
rank[i]<rank[c] 还是
r
a
n
k
[
i
]
≥
r
a
n
k
[
c
]
rank[i]\ge rank[c]
rank[i]≥rank[c] ,
我们都要让
r
a
n
k
[
i
]
rank[i]
rank[i] 和
r
a
n
k
[
c
]
rank[c]
rank[c] 尽可能接近,这样才能使区间的最小值最大。
问题转化成:
(1)求一个
i
∈
[
a
,
b
−
m
i
d
+
1
]
,
r
a
n
k
[
i
]
≤
r
a
n
k
[
c
]
i\in[a,b-mid+1],rank[i]\le rank[c]
i∈[a,b−mid+1],rank[i]≤rank[c] 使得
r
a
n
k
[
i
]
rank[i]
rank[i] 最大。
(2)求一个
i
∈
[
a
,
b
−
m
i
d
+
1
]
,
r
a
n
k
[
i
]
≥
r
a
n
k
[
c
]
i\in[a,b-mid+1],rank[i]\ge rank[c]
i∈[a,b−mid+1],rank[i]≥rank[c] 使得
r
a
n
k
[
i
]
rank[i]
rank[i] 最小。
也就是求区间内某个数的前驱后继。
可以以原串中的位置为下标,
r
a
n
k
rank
rank 为权值建立主席树,通过查询 区间排名 / 区间内排名为
k
k
k 的数 实现求前驱后继。
复杂度
O
(
n
log
2
n
)
O(n\log^2n)
O(nlog2n) 。
Code
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
#define Rof(i, a, b) for (i = a; i >= b; i--)
#define Pow(k, n) for (k = 1; k < n; k <<= 1, swap(x, y))
using namespace std;
inline int read() {
int res = 0; bool bo = 0; char c;
while (((c = getchar()) < '0' || c > '9') && c != '-');
if (c == '-') bo = 1; else res = c - 48;
while ((c = getchar()) >= '0' && c <= '9')
res = (res << 3) + (res << 1) + (c - 48);
return bo ? ~res + 1 : res;
}
const int N = 1e5 + 5, M = 3e6 + 5, LogN = 20;
int n, m, sa[N], rank[N], height[N], w[N], rt[N], QAQ, RMQ[N][LogN], Log[N];
char s[N];
struct cyx {
int lc, rc, sum;
} T[M];
void ins(int y, int &x, int l, int r, int p) {
T[x = ++QAQ] = T[y]; T[x].sum++;
if (l == r) return;
int mid = l + r >> 1;
if (p <= mid) ins(T[y].lc, T[x].lc, l, mid, p);
else ins(T[y].rc, T[x].rc, mid + 1, r, p);
}
int rankl(int y, int x, int l, int r, int p) {
if (l == r) return T[x].sum - T[y].sum;
int mid = l + r >> 1;
if (p <= mid) return rankl(T[y].lc, T[x].lc, l, mid, p);
else return rankl(T[y].rc, T[x].rc, mid + 1, r, p)
+ T[T[x].lc].sum - T[T[y].lc].sum;
}
int rankr(int y, int x, int l, int r, int p) {
if (l == r) return T[x].sum - T[y].sum;
int mid = l + r >> 1;
if (p > mid) return rankr(T[y].rc, T[x].rc, mid + 1, r, p);
else return rankr(T[y].lc, T[x].lc, l, mid, p)
+ T[T[x].rc].sum - T[T[y].rc].sum;
}
int getrankl(int y, int x, int l, int r, int p) {
if (l == r) return l;
int delta = T[T[x].lc].sum - T[T[y].lc].sum, mid = l + r >> 1;
if (p <= delta) return getrankl(T[y].lc, T[x].lc, l, mid, p);
else return getrankl(T[y].rc, T[x].rc, mid + 1, r, p - delta);
}
int getrankr(int y, int x, int l, int r, int p) {
if (l == r) return l;
int delta = T[T[x].rc].sum - T[T[y].rc].sum, mid = l + r >> 1;
if (p <= delta) return getrankr(T[y].rc, T[x].rc, mid + 1, r, p);
else return getrankr(T[y].lc, T[x].lc, l, mid, p - delta);
}
int pre(int l, int r, int p) {
int rk = rankl(rt[l - 1], rt[r], 1, n, p);
if (!rk) return 0;
return getrankl(rt[l - 1], rt[r], 1, n, rk);
}
int suf(int l, int r, int p) {
int rk = rankr(rt[l - 1], rt[r], 1, n, p);
if (!rk) return 0;
return getrankr(rt[l - 1], rt[r], 1, n, rk);
}
void cyxisdalao() {
int i, j, k, m = 26, *x = rank, *y = height;
For (i, 1, n) w[x[i] = s[i] - 'a' + 1]++;
For (i, 2, m) w[i] += w[i - 1];
For (i, 1, n) sa[w[x[i]]--] = i;
Pow(k, n) {
int tt = 0;
For (i, n - k + 1, n) y[++tt] = i;
For (i, 1, n) if (sa[i] > k) y[++tt] = sa[i] - k;
memset(w, 0, sizeof(w));
For (i, 1, n) w[x[i]]++;
For (i, 2, m) w[i] += w[i - 1];
Rof (i, n, 1) sa[w[x[y[i]]]--] = y[i];
m = 0;
For (i, 1, n) {
int u = sa[i], v = sa[i - 1];
y[u] = x[u] != x[v] || x[u + k] != x[v + k] ? ++m : m;
}
if (m == n) break;
}
if (y != rank) copy(y, y + n + 1, rank);
k = height[1] = 0;
For (i, 1, n) {
if (k) k--;
int x = sa[rank[i] - 1];
while (s[x + k] == s[i + k]) k++;
height[rank[i]] = k;
}
Log[0] = -1;
For (i, 1, n) Log[i] = Log[i >> 1] + 1;
For (i, 2, n) RMQ[i][0] = height[i];
For (j, 1, 17) For (i, 2, n - (1 << j) + 1)
RMQ[i][j] = min(RMQ[i][j - 1], RMQ[i + (1 << j - 1)][j - 1]);
}
int LCP(int x, int y) {
if (x == y) return n - x + 1;
x = rank[x]; y = rank[y];
if (x > y) swap(x, y); x++;
int k = Log[y - x + 1];
return min(RMQ[x][k], RMQ[y - (1 << k) + 1][k]);
}
int main() {
int i, a, b, c, d;
n = read(); m = read();
scanf("%s", s + 1);
cyxisdalao();
For (i, 1, n) ins(rt[i - 1], rt[i], 1, n, rank[i]);
while (m--) {
a = read(); b = read(); c = read(); d = read();
int l = 1, r = min(b - a + 1, d - c + 1);
while (l <= r) {
int mid = l + r >> 1, p = pre(a, b - mid + 1, rank[c]),
s = suf(a, b - mid + 1, rank[c]), rp = 0;
if (p) rp = max(rp, LCP(sa[p], c));
if (s) rp = max(rp, LCP(sa[s], c));
if (rp >= mid) l = mid + 1;
else r = mid - 1;
}
printf("%d\n", r);
}
return 0;
}