QOJ 7364 回文
首先考虑到这个询问是一个“截取”的形式,所以如果只保留下最长的一些回文串是做不了的。
于是就应该需要维护好每一个回文串的信息,便可以考虑 manacher。
接下来涉及到的下标都默认是经过 manacher 处理后的。
考虑已经对于每个 \(i\) 知道了 \(p_i\)(\([i - p_i, i + p_i]\) 这一段为以 \(i\) 为中心的最长回文串)。
接下来对于询问 \([l, r]\) 该怎么处理。
那么此时就可以考虑每个 \(i\in [l, r]\) 对答案的贡献,那么一共有 \(3\) 个限制:是回文;左边界不过 \(l\);右边界不过 \(r\)。
即 \(ans_i = \min\{p_i, i - l, r - i\}\),\(ans = \max\limits_{i = l}^r \{ans_i\}\)。
如果考虑直接拆这个偏序关系的话会很难维护,于是考虑其他的做法。
考虑 \({ans_i}\) 的 \({\min}\) 和最后 \({ans}\) 的 \({\max}\) 这两个关系。
因为 \(ans_i = \min\{p_i, i - l, r_i\}\),那么也就可以得到 \(p_i, i - l, r - i\ge ans_i\)。
那么因为 \(ans = \max\{ans_i\}\),那就说明存在 \(ans_i\ge ans\)(这里只能取等),那就推出了 \(i\in [l, r]\),\(p_i, i - l, r - i\ge ans\)。
于是发现在刚刚的讨论中找到了一个 \({ans}\) 合法的条件。
于是就可以考虑二分 \(ans'\),判定这个 \(ans'\) 是否合法(\(ans\) 是否 \(\ge ans'\))。
此时再来回看判定条件:存在 \(i\in [l, r], p_i, i - l, r - i\ge ans'\)。
那么此时有三个限制还是不好做,所以考虑利用 \({ans'}\) 便捷的直接满足一些限制。
对于这个式子,如果来满足 \(p_i\) 这个限制,那么对于 \(i - l, r - i\) 还是不太方便,当然也是可以做的(整体二分 / 主席树)。
因为本身询问就是个区间 \([l, r]\),所以自然的,如果接下来考虑的也是个区间就好了。所以可以考虑 \(i - l, r - i\ge ans'\) 这个限制,可以得到 \(l + ans'\le i\le r - ans'\)。
于是此时就得到了 \(i\) 的一个合法的范围,限制就只剩下了一个 \(p_i\ge ans'\)。
此时因为要解决的问题是存在性问题,所以可以直接贪心的考虑。具体来说,如果存在 \(p_i\ge ans'\),那么一定有这个区间内的 \(\max \{p_i\}\ge ans'\)。
于是整个判定问题被推导成了一个区间最值问题。
因为一共有 \(n\) 个元素,\(q\log n\) 次查最值,可以用 st 表维护来平衡复杂度,做到 \(\mathcal{O}((n + q)\log n)\)。
#include<bits/stdc++.h>
constexpr int maxn = 1e6 + 10;
int n; char str[maxn], s[maxn];
int p[maxn];
int mx[21][maxn];
int main() {
scanf("%s", str + 1);
n = strlen(str + 1);
s[0] = '$', s[1] = '#';
for (int i = 1; i <= n; i++) {
s[i * 2] = str[i], s[i * 2 + 1] = '#';
}
s[n * 2 + 2] = '&';
n = n * 2 + 1;
for (int i = 1, mid = 0, mxr = 0; i <= n; i++) {
if (i <= mxr) {
p[i] = std::min(mxr - i, p[mid * 2 - i]);
}
while (s[i - p[i] - 1] == s[i + p[i] + 1]) p[i]++;
if (i + p[i] > mxr) {
mid = i, mxr = i + p[i];
}
}
memcpy(mx[0], p, sizeof(mx[0]));
for (int i = 1; i < 21; i++) {
for (int l = 1, r = 1 << i; r <= n; l++, r++) {
mx[i][l] = std::max(mx[i - 1][l], mx[i - 1][l + (1 << i - 1)]);
}
}
auto query =
[&](int l, int r) {
int k = std::__lg(r - l + 1);
return std::max(mx[k][l], mx[k][r - (1 << k) + 1]);
};
int Q; scanf("%d", &Q);
for (int l, r; Q--; ) {
scanf("%d%d", &l, &r);
int sl = 1, sr = r - l + 1, ans = 1;
l = l * 2 - 1, r = r * 2 + 1;
while (sl <= sr) {
int mid = sl + sr >> 1;
if (query(l + mid, r - mid) >= mid) {
ans = mid, sl = mid + 1;
} else {
sr = mid - 1;
}
}
printf("%d\n", ans);
}
return 0;
}
浙公网安备 33010602011771号