「luogu4096」[HE_TJOI2016] 字符串
「luogu4096」[HE_TJOI2016] 字符串
这是一道偏综合的题目(SA+主席树)。
先后缀排序并求出 \(height\) 数组的区间最小值(st表);
把 \(sa\) 当做权值建主席树;
二分答案 \(len\);
二分找到包含 \(rank_c\) 的最大的 \(rank\) 区间,使得该区间的 \(height\) 最小值不小于 \(len\),设该区间的左右端点为 \(p1,p2\);
在主席树中查询 \(p1\) 和 \(p2\) 两个历史版本之间有没有权值在 \(a\) ~ (\(b - len + 1)\),以此判断 \(len\) 是否可行。
代码如下(记得开\(O2\)):
// luogu-judger-enable-o2
#include <cstdio>
#include <cstring>
#include <algorithm>
int in() {
int x = 0; char c = getchar(); bool f = 0;
while (c < '0' || c > '9')
f |= c == '-' , c = getchar();
while (c >= '0' && c <= '9')
x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
return f ? -x : x;
}
const int N = 1e5 + 5;
char s[N];
int n, m, q, sa[N], rnk[N], tp[N << 1], hei[N], buk[N];
int bi[N], lg[N], st_min[19][N];
//Get suffix array begin
void Tsort() {
std::fill(buk + 1, buk + 1 + m, 0);
for (int i = 1; i <= n; ++i)
++buk[rnk[i]];
for (int i = 1; i <= m; ++i)
buk[i] += buk[i - 1];
for (int i = n; i; --i)
sa[buk[rnk[tp[i]]]--] = tp[i];
}
void Ssort() {
m = 256;
for (int i = 1; i <= n; ++i)
tp[i] = i, rnk[i] = s[i];
Tsort();
for (int w = 1, p = 0; p < n; w <<= 1, m = p) {
p = 0;
for (int i = n - w + 1; i <= n; ++i)
tp[++p] = i;
for (int i = 1; i <= n; ++i)
if (sa[i] > w)
tp[++p] = sa[i] - w;
Tsort();
std::copy(rnk + 1, rnk + 1 + n, tp + 1);
rnk[sa[1]] = p = 1;
for (int i = 2; i <= n; ++i)
rnk[sa[i]] = tp[sa[i]] == tp[sa[i - 1]] && tp[sa[i] + w] == tp[sa[i - 1] + w] ? p : ++p;
}
int k = 0;
for (int i = 1, j; i <= n; ++i) {
if (rnk[i] == 1)
continue;
if (k) --k;
j = sa[rnk[i] - 1];
while (i + k <= n && j + k <= n && s[i + k] == s[j + k])
++k;
hei[rnk[i]] = st_min[0][rnk[i]] = k;
}
}
//Get suffix array end
//persistable segment tree begin
struct persistable_segment_tree {
#define lson tl, mid, c[pre][0], c[p][0]
#define rson mid + 1, tr, c[pre][1], c[p][1]
int tot;
int t[N * 20], c[N * 20][2], rt[N];
void modify(int pos, int tl, int tr, int pre, int &p) {
p = ++tot;
t[p] = t[pre] + 1;
if (tl == tr)
return ;
c[p][0] = c[pre][0], c[p][1] = c[pre][1];
int mid = (tl + tr) >> 1;
if (mid >= pos)
modify(pos, lson);
else
modify(pos, rson);
}
bool query(int l, int r, int tl, int tr, int pre, int p) {
if (l <= tl && tr <= r) {
return t[p] - t[pre] > 0;
}
int mid = (tl + tr) >> 1;
if (mid < l)
return query(l, r, rson);
if (mid >= r)
return query(l, r, lson);
else
return query(l, r, lson) | query(l, r, rson);
}
#undef lson
#undef rson
} T;
//persistable segment tree end
//prepare begin (st_min, tree)
void prep() {
bi[0] = 1;
for (int i = 1; i < 17; ++i)
bi[i] = bi[i - 1] << 1;
for (int i = 2; i <= n; ++i)
lg[i] = lg[i >> 1] + 1;
for (int k = 1; k < 17; ++k)
for (int i = 1; i + bi[k] - 1 <= n; ++i)
st_min[k][i] = std::min(st_min[k - 1][i], st_min[k - 1][i + bi[k - 1]]);
for (int i = 1; i <= n; ++i) {
T.modify(sa[i], 1, n, T.rt[i - 1], T.rt[i]);
}
}
int query_lcp(int l, int r) {
if (l > r)
return 0x3f3f3f3f;
int k = lg[r - l + 1];
return std::min(st_min[k][l], st_min[k][r - bi[k] + 1]);
}
//prepare end (st_min, tree)
//binary search begin
bool chk(int x, int y, int p, int lim) {
int l = 1, r = p, mid, p1, p2;
while (l < r) {
mid = (l + r) >> 1;
if (query_lcp(mid + 1, p) < lim)
l = mid + 1;
else
r = mid;
}
p1 = l;
l = p, r = n;
while (l < r) {
mid = (l + r + 1) >> 1;
if (query_lcp(p + 1, mid) >= lim)
l = mid;
else
r = mid - 1;
}
p2 = l;
return T.query(x, y, 1, n, T.rt[p1 - 1], T.rt[p2]);
}
//binary search end
int main() {
n = in(), q = in();
scanf(" %s", s + 1);
Ssort(), prep();
int a, b, c, d, l, r, mid;
while (q--) {
a = in(), b = in(), c = in(), d = in();
l = 0, r = std::min(b - a + 1, d - c + 1);
while (l < r) {
mid = (l + r + 1) >> 1;
if (chk(a, b - mid + 1, rnk[c], mid))
l = mid;
else
r = mid - 1;
}
printf("%d\n", l);
}
return 0;
}