题意: 找出现至少k次的子串的最大长度.
思路:据贪心策略,子串变长答案不会减小,所以可以看做是求k个后缀的LCP.因为要求LCP的最大值,而LCP(suffix(sa[i]),suffix(sa[j]))=min{height[k]}(i<j,k∈[i,j]),显然当这k个后缀在排名上连续的时候可以取得最小值.因为若排名不连续,那么涵盖的取最小值的范围一定会变大,而当取值范围变大最小值只会变小.所以这个贪心是正确的.
二分 + 后缀数组(height数组)
我们知道,后缀(j)和后缀(k)的 最 长 公 共 前 缀 为height[rank[j]+1],
height[rank[j]+2],height[rank[j]+3],…,height[rank[k]]中的最小值(设rank[j]<rank[k])。
那么设k个后缀中rank的min=l,max=r,k个的最长公共前缀就是min(height[l+1->r])
所以k个后缀在rank上一定是连续的。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 5;
int n, m, k, p;
int s[maxn];
int sa[maxn], rak[maxn], tx[maxn], height[maxn], q[maxn];
struct node {
int x, y, id;
}a[maxn],b[maxn];
void rsort() {
for (int i = 1; i <= m; i++) {
tx[i] = 0;
}
for (int i = 1; i <= n; i++) {
tx[a[i].y]++;
}
for (int i = 1; i <= m; i++) {
tx[i] += tx[i - 1];
}
for (int i = 1; i <= n; i++) {
b[tx[a[i].y]--] = a[i];
}
for (int i = 1; i <= m; i++) {
tx[i] = 0;
}
for (int i = 1; i <= n; i++) {
tx[b[i].x]++;
}
for (int i = 1; i <= m; i++) {
tx[i] += tx[i - 1];
}
for (int i = n; i >= 1; i--) {
a[tx[b[i].x]--] = b[i];
}
}
void solve() {
rsort();
p = 0;
for (int i = 1; i <= n; i++) {
if (a[i].x != a[i - 1].x || a[i].y != a[i - 1].y) {
++p;
}
rak[a[i].id] = p;
}
for (int i = 1; i <= n; i++) {
a[i].x = rak[i];
a[i].id = sa[rak[i]] = i;
a[i].y = 0;
}
m = p;
}
void ssort() {
for (int i = 1; i <= n; i++) {
a[i].x = a[i].y = s[i];
a[i].id = i;
m = max(m, s[i]);
}
solve();
for (int j = 1; j <= n; j <<= 1) {
for (int i = 1; i + j <= n; i++) {
a[i].y = a[i + j].x;
}
solve();
if (p == n) {
break;
}
}
}
void get_Height() {
int k = 0;
for (int i = 1; i <= n; i++) {
if (k) {
k--;
}
int j = sa[rak[i] - 1];
while (i + k <= n && j + k <= n && s[i + k] == s[j + k]) {
k++;
}
height[rak[i]] = k;
}
}
/* 5 2 1 2 1 2 1 */
int check(int x) {
int i = 1;
while (1) {
for (; i <= n && height[i] < x; i++);
if (i > n) {
break;
}
int cnt = 1;
for (; i <= n && height[i] >= x; cnt++, i++);
if (cnt >= k) {
return 1;
}
}
return 0;
}
int main() {
scanf("%d%d", &n, &k);
for (int i = 1; i <= n; i++) {
scanf("%d", &s[i]);
}
ssort(); get_Height();
int l = 1, r = n;
int ans = 0;
while (l <= r) {
int mid = (l + r) / 2;
if (check(mid)) {
l = mid + 1;
} else {
r = mid - 1;
}
}
printf("%d\n", l - 1);
return 0;
}