题目链接:https://www.luogu.com.cn/problem/P2852
今天打算复习一下后缀数组
后缀数组裸题。
紧扣后缀数组性质,
L
C
P
(
s
a
[
i
]
,
s
a
[
j
]
)
=
m
i
n
(
h
e
i
g
h
t
[
k
]
)
k
<
i
<
=
j
LCP(sa[i],sa[j])=min(height[k]) \quad k<i<=j
LCP(sa[i],sa[j])=min(height[k])k<i<=j
还有一个性质:任意一个后缀的前缀是原串中唯一的一个子串
然后就可以先预处理出后缀数组,然后二分长度.
检查的时候就扫一遍
h
e
i
g
h
t
height
height数组,看看是否存在一段长度不少于
k
k
k的区间的
h
e
i
g
h
t
height
height均不小于
l
e
n
len
len 即可
因为由前文后缀数组的性质:当 L C P ( s a [ l ] , s a [ r ] ) LCP(sa[l],sa[r]) LCP(sa[l],sa[r])大于等于 k k k时,区间 [ l , r ] 1 < = l , r < = n [l,r]\quad 1<=l,r<=n [l,r]1<=l,r<=n中每两个数的 L C P LCP LCP都大于等于 k k k
C o d e Code Code
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cstring>
using namespace std;
const int MAXN = 2e4;
int str[MAXN + 10], sa[MAXN + 10], rk[MAXN + 10], id[MAXN + 10], height[MAXN + 10];
inline int read();
namespace SA{
void Rsort(int, int);
void get_sa(int);
void get_height(int);
int get_ans(int, int);
}
using namespace SA;
int main(){
//freopen ("std.in", "r",stdin);
//freopen ("std.out", "w", stdout);
int n, k, len = 0;
n = read(), k = read();
for (register int i = 1; i <= n; ++i) str[i] = read();
get_sa(n); get_height(n);
int l = 1, r = n;
while (l <= r){
int mid = l + r >> 1;
if (get_ans(n, mid) >= k) l = (len = mid) + 1;
else r = mid - 1;
}
printf("%d\n", len);
return 0;
}
inline int read(){
int x = 0;
char c = getchar();
while (!isdigit(c))c = getchar();
while (isdigit(c))x = (x << 1) + (x << 3) + (c & 15), c = getchar();
return x;
}
namespace SA{
void Rsort(int n, int m){
static int buck[MAXN + 10];
for (register int i = 1; i <= m; ++i) buck[i] = 0;
for (register int i = 1; i <= n; ++i) ++buck[rk[i]];
for (register int i = 1; i <= m; ++i) buck[i] += buck[i - 1];
for (register int i = n; i >= 1; --i) sa[ buck[rk[id[i]]]-- ] = id[i];
}
void get_sa(int n){
int m = 127, p = 0;
for (register int i = 1; i <= n; ++i){
rk[i] = str[i];
id[i] = i;
}
Rsort(n, m);
for (register int len = 1; p < n; m = p, len <<= 1){
p = 0;
for (register int i = 1; i <= len; ++i) id[++p] = n - len + i;
for (register int i = 1; i <= n; ++i)
if (sa[i] > len) id[++p] = sa[i] - len;
Rsort(n, m);
std::swap(rk, id);
rk[sa[1]] = p = 1;
for (register int i = 2; i <= n; ++i)
rk[sa[i]] = (id[sa[i]] == id[sa[i - 1]] && id[sa[i] + len] == id[sa[i - 1] + len])? p : ++p;
}
}
void get_height(int n){
int k = 0;
for (register int i = 1; i <= n; ++i){
if (k) --k;
int j = sa[rk[i] - 1];
while (str[i + k] == str[j + k]) ++k;
height[rk[i]] = k;
}
}
int get_ans(int n, int len){
int cnt = 1, l = 99999999, ans = 1;
for (register int i = 2; i <= n; ++i){
l = min(l, height[i]);
if (l < len){
ans = max(ans, cnt);
cnt = 1; l = 99999999;
}
else ++cnt;
}
ans = max(ans, cnt);
return ans;
}
}