题目描述
题解
我们知道后缀数组可以求出一段连续区间字符串的最长公共前缀。
我们可以将题目转化为:求解一堆数字的后缀,在个数大于等于 k k k的情况下 L C P LCP LCP最大。
我们此时可以用后缀数组来解决这个问题,二分 L C P LCP LCP值 m i d mid mid,我们就可以考虑将所有除起始位置以外的所有 h e i g h t height height值大于等于 m i d mid mid的后缀分成一组,判断这些后缀组中的可数是否大于等于给定的 k k k即可。
代码如下:
#include <bits/stdc++.h>
using namespace std;
const int N = 100000;
struct SArray
{
int Max = 0, n, p, k;
int cnt[N], SA[N], rk[N], a[N], tmp[N], t[N], h[N];
void init(void)
{
scanf("%d %d", &n, &k);
for (int i=1;i<=n;++i)
scanf("%d", a+i);
return;
}
void sort1(void)
{
for (int i=1;i<=n;++i) Max = max(Max,a[i]), cnt[a[i]] ++;
for (int i=1;i<=Max;++i) cnt[i] += cnt[i-1];
for (int i=n;i;--i) SA[cnt[a[i]] --] = i;
rk[SA[1]] = p = 1;
for (int i=2;i<=n;++i)
rk[SA[i]] = a[SA[i]] == a[SA[i-1]] ? p : ++p;
return;
}
void sort2(void)
{
for (int k=1;k<=n;k<<=1)
{
memset(cnt,0,sizeof cnt);
for (int i=1;i<=n;++i) cnt[rk[i+k]] ++;
for (int i=1;i<=p;++i) cnt[i] += cnt[i-1];
for (int i=n;i;--i) tmp[cnt[rk[i+k]] --] = i;
memset(cnt,0,sizeof cnt);
for (int i=1;i<=n;++i) cnt[rk[tmp[i]]] ++;
for (int i=1;i<=p;++i) cnt[i] += cnt[i-1];
for (int i=n;i;--i) SA[cnt[rk[tmp[i]]] --] = tmp[i];
t[SA[1]] = p = 1;
for (int i=2;i<=n;++i)
t[SA[i]] = rk[SA[i]] == rk[SA[i-1]] && rk[SA[i]+k] == rk[SA[i-1]+k] ? p : ++p;
for (int i=1;i<=n;++i) rk[i] = t[i];
}
return;
}
void get_height(void)
{
for (int i=1,j=0;i<=n;++i)
{
if (rk[i] == 1) continue;
while (a[i+j] == a[SA[rk[i]-1]+j]) j ++;
h[rk[i]] = j; j -= j ? 1 : 0;
}
return;
}
void work(void)
{
init();
sort1();
sort2();
get_height();
return;
}
} arr;
bool check(int x)
{
int cnt = 0;
for (int i=1;i<=arr.n;++i)
{
arr.h[i] < x ? cnt = 1 : cnt ++;
if (cnt >= arr.k) return 1;
}
return 0;
}
int main(void)
{
arr.work();
int l = 0, r = 1e9;
while (l + 1 < r)
{
int mid = l+r >> 1;
check(mid) ? l = mid : r = mid;
}
cout<<(check(r) ? r : l)<<endl;
return 0;
}