【GDOI2020模拟4.11】黑红兔(brr) (SA+字符串性质+可持久化线段树)
\(n\le 500000\)
https://gmoj.net/senior/#main/show/6555
一个发现是我们肯定可以选长度为\(1,2,3,…\)的一组解。
那么长度不超过\(\sqrt {2n}\),做一个dp,设\(f[i][j]\)表示\(j\)为开头,长度为\(i\),是否可以。
用hash去找相同的子串,用个指针维护能转移区域的max值,即可做到\(O(n \sqrt n)\)
接下来需要发现更深的性质:
考虑以\(j\)开头的,如果有长度为\(i\)的解,那么也一定有长度为\(i-1\)的解,所以直接设\(f[j]\)表示以j开头最长是多少。
从后往前做,对于\(f[j]\)的值,二分\(mid\),相当于在\(k\in[j+mid,n]\)中找一个\(k\),满足\(max(lcp(s[j..n],s[k..n]),lcp[s[j+1..n],s[k..n]])\ge mid-1\),且\(f[k]+1\ge mid\)
\(lcp\)的限制建出\(sa\)之后就是在一个区间里,至于\(k\in[j+mid,n]\),用可持久化线段树找区间最大值就好了。
\(O(n~log^2~n)\)还是过不了的。
类似于\(SA\)的\(height\)的求法,对\(f\)显然有\(f[j+1]\ge f[j]-1->f[j]\le f[j+1]+1\),这样就不用二分了。
Code:
#include<bits/stdc++.h>
#define fo(i, x, y) for(int i = x, _b = y; i <= _b; i ++)
#define ff(i, x, y) for(int i = x, _b = y; i < _b; i ++)
#define fd(i, x, y) for(int i = x, _b = y; i >= _b; i --)
#define ll long long
#define pp printf
#define hh pp("\n")
using namespace std;
const int N = 5e5 + 5;
int n;
char s[N];
int m, sa[N], rk[N], tp[N], tax[N], he[N];
void rsort() {
fo(i, 1, m) tax[i] = 0;
fo(i, 1, n) tax[rk[tp[i]]] ++;
fo(i, 1, m) tax[i] += tax[i - 1];
fd(i, n, 1) sa[tax[rk[tp[i]]] --] = tp[i];
}
int cmp(int *f, int x, int y, int z) { return f[x] == f[y] && f[x + z] == f[y + z];}
int f[20][N];
void build() {
s[n + 1] = -1;
fo(i, 1, n) rk[i] = s[i], tp[i] = i;
m = 255, rsort();
for(int w = 1, p = 0; p < n; w *= 2) {
p = 0; fo(i, n - w + 1, n) tp[++ p] = i;
fo(i, 1, n) if(sa[i] > w) tp[++ p] = sa[i] - w;
m = p, rsort();
fo(i, 1, n) tp[i] = rk[i];
rk[sa[1]] = p = 1;
fo(i, 2, n) rk[sa[i]] = cmp(tp, sa[i - 1], sa[i], w) ? p : ++ p;
}
int j, k = 0;
for(int i = 1; i <= n; he[rk[i ++]] = k) {
for(k ? k -- : 0, j = sa[rk[i] - 1]; s[i + k] == s[j + k]; k ++);
}
fo(i, 1, n) f[0][i] = he[i];
fo(j, 1, 19) fo(i, 1, n) {
f[j][i] = f[j - 1][i];
if(i + (1 << j - 1) <= n) f[j][i] = min(f[j][i], f[j - 1][i + (1 << j - 1)]);
}
// fo(i, 1, n) {
// pp("%d\n", he[i]);
// fo(j, sa[i], n) pp("%c", s[j]);
// hh;
// }
}
int qu(int x, int y) {
int l = log2(y - x + 1);
return min(f[l][x], f[l][y - (1 << l) + 1]);
}
#define i0 t[i].l
#define i1 t[i].r
struct tree {
int l, r, x;
} t[N * 20]; int tt;
int pl, pr, px;
void add(int &i, int x, int y) {
if(y < pl || x > pr) return;
t[++ tt] = t[i]; i = tt;
t[i].x = max(t[i].x, px);
if(x == y) return;
int m = x + y >> 1;
add(i0, x, m); add(i1, m + 1, y);
}
void ft(int i, int x, int y) {
if(!i || y < pl || x > pr) return;
if(x >= pl && y <= pr) { px = max(px, t[i].x); return;}
int m = x + y >> 1;
ft(i0, x, m); ft(i1, m + 1, y);
}
int g[N], dp[N];
void cx(int x, int v) {
pl = x, pr = x;
for(int l = 1, r = x - 1; l <= r; ) {
int m = l + r >> 1;
if(qu(m + 1, x) >= v - 1) pl = m, r = m - 1; else l = m + 1;
}
for(int l = x + 1, r = n; l <= r; ) {
int m = l + r >> 1;
if(qu(x + 1, m) >= v - 1) pr = m, l = m + 1; else r = m - 1;
}
}
int pd(int i, int v) {
px = 0;
if(i + v <= n) {
cx(rk[i], v);
ft(g[i + v], 1, n);
if(i < n) {
cx(rk[i + 1], v);
ft(g[i + v], 1, n);
}
}
return px + 1 >= v;
}
void work() {
int ans = 0;
fd(i, n, 1) {
dp[i] = dp[i + 1] + 1;
while(!pd(i, dp[i])) dp[i] --;
ans = max(ans, dp[i]);
g[i] = g[i + 1];
pl = pr = rk[i]; px = dp[i];
add(g[i], 1, n);
}
pp("%d\n", ans);
}
int main() {
freopen("brr.in", "r", stdin);
freopen("brr.out", "w", stdout);
scanf("%s", s + 1);
n = strlen(s + 1);
build();
work();
}
转载注意标注出处:
转自Cold_Chair的博客+原博客地址