本题实质上,就是求序列中最长的出现了至少
k
k
次的子串
而子串实质上就是后缀的前缀,于是我们可以使用后缀数组
鉴于后缀数组中,拥有相同前缀的的一系列后缀会表现为一段连续的区间
而相邻后缀的公共前缀长度又储存在,因此我们只需要求height数组中所有长度为
k−1
k
−
1
的区间的区间最小值的最大值即可。
虽然求区间最小值可以RMQ,然而偷懒的我决定维护单调队列,实际上也快不了多少,但是好写啊
具体实现见代码如下:
// luogu-judger-enable-o2
#include <bits/stdc++.h>
using namespace std;
int n,K,a[20010],tp[20010],SA[20010],r[20010],t[1000010],q[20010],h[20010],qh,qt,ret;
void Sort(int m)
{
memset(t,0,sizeof(int)*(m+1));
for(int i=1;i<=n;i++)
{
t[r[i]]++;
}
for(int i=0;i<=m;i++)
{
t[i]+=t[i-1];
}
for(int i=n;i;i--)
{
SA[t[r[tp[i]]]--]=tp[i];
}
}
int main()
{
cin>>n>>K;
for(int i=1;i<=n;i++)
{
cin>>a[i];
tp[i]=i;
r[i]=a[i];
}
Sort(1000000);
for(int i=1,m=1000000,p=0;p<n;i<<=1,m=p)
{
p=0;
for(int j=n-i+1;j<=n;j++)
{
tp[++p]=j;
}
for(int j=1;j<=n;j++)
{
if(SA[j]>i)
{
tp[++p]=SA[j]-i;
}
}
Sort(m);
swap(tp,r);
r[SA[1]]=p=1;
for(int j=2;j<=n;j++)
{
r[SA[j]]=((tp[SA[j]]==tp[SA[j-1]])&&(tp[SA[j]+i]==tp[SA[j-1]+i])?p:++p);
}
}
for(int i=1,j,k=0;i<=n;h[r[i++]]=k)
{
for(k=(k?k-1:k),j=SA[r[i]-1];a[i+k]==a[j+k];k++);
}
for(int i=1;i<=n;i++)
{
while(qh<qt&&h[q[qt-1]]>h[i])qt--;
q[qt++]=i;
if(q[qh]<=i-K+1)qh++;
if(i>=K-1)ret=max(ret,h[q[qh]]);
}
cout<<ret<<endl;
return 0;
}