Description
有一个长度为
n
n
n的字符串
s
s
s。定义一种划分为
k
k
k个字符串构成的序列
t
k
{tk}
tk,满足:
对于
i
>
1
i > 1
i>1,
t
i
ti
ti 是
t
i
−
1
ti−1
ti−1的子串。
s
=
u
1
t
1
u
2
t
2
⋅
⋅
⋅
u
k
t
k
u
k
+
1
s = u1t1u2t2 · · · uktkuk+1
s=u1t1u2t2⋅⋅⋅uktkuk+1,其中
u
i
ui
ui是任意字符串,可以为空。
求所有划分中
k
k
k最大是多少。
Sample Input
2
aa
Sample Output
1
首先让我们来考虑一些性质。
最优解情况下,
t
t
t即字符串集合的长度肯定是递减的。
否则你可以给一个字符到相邻的
u
u
u中。
一个答案于是可以由第一个位置
i
i
i,以及第一个字符串长度为
j
j
j表示出来,因为我们不关心后面的东西。
于是你考虑设
f
[
i
]
[
j
]
f[i][j]
f[i][j]为以
i
i
i为第一个位置,以及第一个字符串长度为
j
j
j是否存在,利用
h
a
s
h
hash
hash可以做到
O
(
n
n
)
O(n\sqrt n)
O(nn)。
太慢了,考虑优化。
我们可以简化一下状态,直接设
f
[
i
]
f[i]
f[i]为以
i
i
i开头的最长的
j
j
j。
那么你对于每一个
i
i
i考虑二分答案,每次二分一个长度
j
j
j对应的就是后缀数组的一段区间,就是询问后面的某一段区间是否存在
f
f
f值大于等于
j
−
1
j-1
j−1的,用一个主席树可以实现。
复杂度为
O
(
n
l
o
g
2
n
)
O(nlog^2n)
O(nlog2n)。
但其实可以做到
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)
其实你是可以轻松得到一个性质
f
[
i
]
<
=
f
[
i
+
1
]
+
1
f[i]<=f[i+1]+1
f[i]<=f[i+1]+1
于是你直接暴力搞就好了,因为
f
f
f值的总变化量不超过
n
n
n。
#include <ctime>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
typedef long long LL;
int _max(int x, int y) {return x > y ? x : y;}
int _min(int x, int y) {return x < y ? x : y;}
int read() {
int s = 0, f = 1; char ch = getchar();
while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
return s * f;
}
void put(int x) {
if(x >= 10) put(x / 10);
putchar(x % 10 + '0');
}
struct tnode {
int lc, rc, c;
} t[21 * 500010]; int cnt, rt[500010];
char ss[500010];
int n, m, sa[500010], yy[500010], height[500010], Rank[500010], tt[500010], Rsort[500010];
int st[20][500010], Log[500010], f[500010], bin[20];
void get_sa() {
for(int i = 1; i <= n; i++) Rank[i] = ss[i] - 'a' + 1;
memset(Rsort, 0, sizeof(Rsort));
for(int i = 1; i <= n; i++) Rsort[Rank[i]]++;
for(int i = 1; i <= m; i++) Rsort[i] += Rsort[i - 1];
for(int i = n; i >= 1; i--) sa[Rsort[Rank[i]]--] = i;
int ln = 1, p = 0;
while(p < n) {
int k = 0;
for(int i = n - ln + 1; i <= n; i++) yy[++k] = i;
for(int i = 1; i <= n; i++) if(sa[i] - ln > 0) {
yy[++k] = sa[i] - ln;
}
memset(Rsort, 0, sizeof(Rsort));
for(int i = 1; i <= n; i++) Rsort[Rank[yy[i]]]++;
for(int i = 1; i <= m; i++) Rsort[i] += Rsort[i - 1];
for(int i = n; i >= 1; i--) sa[Rsort[Rank[yy[i]]]--] = yy[i];
for(int i = 1; i <= n; i++) tt[i] = Rank[i];
p = 1; Rank[sa[1]] = 1;
for(int i = 2; i <= n; i++) {
if(tt[sa[i]] != tt[sa[i - 1]] || tt[sa[i] + ln] != tt[sa[i - 1] + ln]) p++;
Rank[sa[i]] = p;
} ln *= 2, m = p;
}
}
int lcp(int l, int r) {
l++;
if(l > r) return n;
int len = r - l + 1;
return _min(st[Log[len]][l], st[Log[len]][r - bin[Log[len]] + 1]);
}
void get_height() {
int k = 0;
for(int i = 1; i <= n; i++) {
int j = sa[Rank[i] - 1];
if(k) k--;
while(ss[i + k] == ss[j + k]) ++k;
height[Rank[i]] = k;
} for(int i = 1; i <= n; i++) st[0][i] = height[i];
Log[1] = 0; for(int i = 2; i <= n; i++) Log[i] = Log[i >> 1] + 1;
bin[0] = 1; for(int i = 1; i <= 19; i++) bin[i] = bin[i - 1] * 2;
for(int i = 1; bin[i] <= n; i++) {
for(int j = 1; j <= n - bin[i] + 1; j++) {
st[i][j] = _min(st[i - 1][j], st[i - 1][j + bin[i - 1]]);
}
}
}
void Link(int &u, int l, int r, int p, int c) {
if(!u) u = ++cnt;
t[u].c = _max(t[u].c, c);
if(l == r) return ;
int mid = (l + r) / 2;
if(p <= mid) Link(t[u].lc, l, mid, p, c);
else Link(t[u].rc, mid + 1, r, p, c);
}
void Merge(int &u1, int u2) {
if(!u1 || !u2) {u1 = u1 + u2; return ;}
t[u1].c = _max(t[u1].c, t[u2].c);
Merge(t[u1].lc, t[u2].lc);
Merge(t[u1].rc, t[u2].rc);
}
int getmx(int u, int l, int r, int ll, int rr) {
if(l == ll && r == rr) return t[u].c;
int mid = (l + r) / 2;
if(rr <= mid) return getmx(t[u].lc, l, mid, ll, rr);
else if(ll > mid) return getmx(t[u].rc, mid + 1, r, ll, rr);
else return _max(getmx(t[u].lc, l, mid, ll, mid), getmx(t[u].rc, mid + 1, r, mid + 1, rr));
}
bool check(int x, int len) {
int hh = Rank[x];
int l = 1, r = hh, L = hh;
while(l <= r) {
int mid = (l + r) / 2;
if(lcp(mid, hh) >= len - 1) r = mid - 1, L = mid;
else l = mid + 1;
} l = hh, r = n; int R = hh;
while(l <= r) {
int mid = (l + r) / 2;
if(lcp(hh, mid) >= len - 1) l = mid + 1, R = mid;
else r = mid - 1;
} if(getmx(rt[x + len], 1, n, L, R) >= len - 1) return 1;
hh = Rank[x + 1];
l = 1, r = hh, L = hh;
while(l <= r) {
int mid = (l + r) / 2;
if(lcp(mid, hh) >= len - 1) r = mid - 1, L = mid;
else l = mid + 1;
} l = hh, r = n, R = hh;
while(l <= r) {
int mid = (l + r) / 2;
if(lcp(hh, mid) >= len - 1) l = mid + 1, R = mid;
else r = mid - 1;
} if(getmx(rt[x + len], 1, n, L, R) >= len - 1) return 1;
return 0;
}
int main() {
n = read();
scanf("%s", ss + 1);
m = 27; get_sa();
get_height();
f[n] = 1; Link(rt[n], 1, n, Rank[n], 1);
for(int i = n - 1; i >= 1; i--) {
f[i] = f[i + 1] + 1;
while(f[i] && !check(i, f[i])) --f[i];
Link(rt[i], 1, n, Rank[i], f[i]);
Merge(rt[i], rt[i + 1]);
} int ans = 0;
for(int i = 1; i <= n; i++) ans = _max(ans, f[i]);
printf("%d\n", ans);
return 0;
}