题目链接
题意:求字符串中出现至少 k 次的子串的最大长度
思路:出现至少k次意味着后缀排序后有至少连续k个后缀的 LCP 是这个子串。
所以,求出每相邻k-1个height[i]的最小值,再求这些最小值的最大值就是答案。
这里用的是multiset维护最小值。
#include<bits/stdc++.h>
using namespace std;
#define Max_N 1000010
#define F(x) ((x)/3+((x)%3==1?0:tb))
#define G(x) ((x)<tb?(x)*3+1:((x)-tb)*3+2)
int wa1[Max_N*3], wb1[Max_N*3], wv1[Max_N*3], ws1[Max_N*3];
int c0(int *r, int a, int b)
{
return r[a] == r[b] && r[a+1] == r[b+1] && r[a+2] == r[b+2];
}
int c12(int k, int *r, int a, int b)
{
if(k == 2) return r[a] < r[b] || r[a] == r[b] && c12(1, r, a+1, b+1);
else return r[a] < r[b] || r[a] == r[b] && wv1[a+1] < wv1[b+1];
}
void sort(int *r, int *a, int *b, int n, int m)
{
int i;
for (i = 0; i < n; i++) wv1[i] = r[a[i]];
for (i = 0; i < m; i++) ws1[i] = 0;
for (i = 0; i < n; i++) ws1[wv1[i]]++;
for (i = 1; i < m; i++) ws1[i] += ws1[i-1];
for (i = n-1; i >= 0; i--) b[--ws1[wv1[i]]] = a[i];
return;
}
void dc3(int *r, int *sa, int n, int m)
{
int i, j, *rn = r + n, *san = sa + n,ta = 0,tb = (n + 1) / 3,tbc = 0,p;
r[n] = r[n+1] = 0;
for (i = 0; i < n; i++) if (i % 3 != 0) wa1[tbc++] = i;
sort(r+2, wa1, wb1, tbc, m);
sort(r+1, wb1, wa1, tbc, m);
sort(r, wa1, wb1, tbc, m);
for (p = 1,rn[F(wb1[0])] = 0,i = 1;i < tbc; i++)
rn[F(wb1[i])] = c0(r, wb1[i-1], wb1[i]) ? p - 1 : p++;
if (p < tbc) dc3(rn, san, tbc, p);
else for (i = 0; i < tbc; i++) san[rn[i]] = i;
for (i = 0; i < tbc; i++) if (san[i] < tb) wb1[ta++] = san[i] * 3;
if (n % 3 == 1) wb1[ta++] = n - 1;
sort(r, wb1, wa1, ta, m);
for (i = 0; i < tbc; i++) wv1[wb1[i] = G(san[i])] = i;
for (i = 0, j = 0, p = 0; i < ta && j < tbc; p++)
sa[p] = c12(wb1[j] % 3, r, wa1[i], wb1[j]) ? wa1[i++] : wb1[j++];
for(; i < ta; p++) sa[p] = wa1[i++];
for(; j < tbc; p++) sa[p] = wb1[j++];
return;
}
// str sa 都要开三倍的;
void da(int str[], int sa[], int rank1[], int height1[], int n, int m)
{
for (int i = n; i < n*3; i++)
str[i] = 0;
dc3(str, sa, n+1, m);
int i, j, k = 0;
for (i = 0; i <= n; i++) rank1[sa[i]] = i;
for (i = 0; i < n; i++)
{
if (k) k--;
j = sa[rank1[i] - 1];
while (str[i+k] == str[j+k]) k++;
height1[rank1[i]] = k;
}
return;
}
int str[3*Max_N];
int sa[3*Max_N];//sa[i]表示将所有后缀排序后第i小的后缀的编号。
int rank1[3*Max_N];//rank1[i]表示后缀i的排名。
int height1[3*Max_N]; //LCP(sa[i],sa[i-1]);
int main()
{
int n,k;
scanf("%d%d",&n,&k);
for(int i=0;i<n;i++)
{
scanf("%d",&str[i]);
}
str[n]=0;
int m=1000001;
da(str, sa, rank1, height1, n, m);
int ans=0;
multiset<int> t;
for (int i = 1; i <=n; i++) {
t.insert(height1[i]);
if (i >= k) t.erase(t.find(height1[i - k+1]));
ans = max(ans, *t.begin());
}
printf("%d\n",ans);
}