原串的所有子串都可以看成某个后缀的前缀
我们考虑建立后缀数组
那么两个相同的子串,所属的后缀在 s a sa sa数组中也是相邻的
换句话说,按照字典序小到大加入后缀,相同的前缀总是连在i一块
那么可以根据这个来二分
#include <bits/stdc++.h>
using namespace std;
#define ULL unsigned long long
const int maxn = 2e6+10;
const ULL base = 131;
int k;
int x[maxn],y[maxn],c[maxn],sa[maxn],n,m,a[maxn],rk[maxn],height[maxn];
void SA()
{
for(int i=1;i<=n;i++) c[x[i]=a[i]]++;
for(int i=2;i<=n;i++) c[i] += c[i-1];
for(int i=n;i>=1;i--) sa[c[x[i]]--] = i;
for(int k=1;k<=n;k<<=1)
{
int num = 0;
for(int i=n-k+1;i<=n;i++) y[++num] = i;
for(int i=1;i<=n;i++) if( sa[i]>k ) y[++num] = sa[i]-k;
for(int i=1;i<=n;i++) c[i] = 0;
for(int i=1;i<=n;i++) c[x[i]]++;
for(int i=2;i<=m;i++) c[i] += c[i-1];
for(int i=n;i>=1;i--) sa[c[x[y[i]]]--] = y[i], y[i] = 0;
swap(x,y);
x[sa[1]] = 1, num = 1;
for(int i=2;i<=n;i++)
x[sa[i]] = ( y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k] )?num:++num;
if( num==n ) break;
m = num;
}
for(int i=1;i<=n;i++) rk[sa[i]] = i;
int k = 0;
for(int i=1;i<=n;i++)
{
if( rk[i]==1 ) continue;
if( k ) k--;
int j = sa[rk[i]-1];
while( j+k<=n&&i+k<=n&&a[i+k]==a[j+k] ) k++;
height[rk[i]] = k;
}
}
bool isok(int mid)
{
int temp = height[2] , now = 0;
if( height[2]>=mid ) now=2;
for(int i=3;i<=n;i++)
{
if( height[i]>=mid )
{
if( now==0 ) now = 2;
else now++;
}
else now = 0;
if( now>=k ) return true;
}
return false;
}
int main()
{
cin >> n >> k;
for(int i=1;i<=n;i++)
cin >> a[i],a[i]++;
m = 1000001;
SA();
int l = 0, r = n-k+1, ans = 0;
while( r>=l )
{
int mid = l+r>>1;
if( isok(mid) ) l = mid+1,ans = mid;
else r = mid-1;
}
cout << ans;
}
但是实际上不需要二分
我们只需要线性扫一遍,维护一个 k − 1 k-1 k−1个元素的单调队列最小值即可
取所有区间的最大值就是答案
#include <bits/stdc++.h>
using namespace std;
#define ULL unsigned long long
const int maxn = 2e6+10;
const ULL base = 131;
int k;
int x[maxn],y[maxn],c[maxn],sa[maxn],n,m,a[maxn],rk[maxn],height[maxn];
void SA()
{
for(int i=1;i<=n;i++) c[x[i]=a[i]]++;
for(int i=2;i<=n;i++) c[i] += c[i-1];
for(int i=n;i>=1;i--) sa[c[x[i]]--] = i;
for(int k=1;k<=n;k<<=1)
{
int num = 0;
for(int i=n-k+1;i<=n;i++) y[++num] = i;
for(int i=1;i<=n;i++) if( sa[i]>k ) y[++num] = sa[i]-k;
for(int i=1;i<=n;i++) c[i] = 0;
for(int i=1;i<=n;i++) c[x[i]]++;
for(int i=2;i<=m;i++) c[i] += c[i-1];
for(int i=n;i>=1;i--) sa[c[x[y[i]]]--] = y[i], y[i] = 0;
swap(x,y);
x[sa[1]] = 1, num = 1;
for(int i=2;i<=n;i++)
x[sa[i]] = ( y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k] )?num:++num;
if( num==n ) break;
m = num;
}
for(int i=1;i<=n;i++) rk[sa[i]] = i;
int k = 0;
for(int i=1;i<=n;i++)
{
if( rk[i]==1 ) continue;
if( k ) k--;
int j = sa[rk[i]-1];
while( j+k<=n&&i+k<=n&&a[i+k]==a[j+k] ) k++;
height[rk[i]] = k;
}
}
bool isok(int mid)
{
int temp = height[2] , now = 0;
if( height[2]>=mid ) now=2;
for(int i=3;i<=n;i++)
{
if( height[i]>=mid )
{
if( now==0 ) now = 2;
else now++;
}
else now = 0;
if( now>=k ) return true;
}
return false;
}
int id[maxn],head=1,tail;
int main()
{
cin >> n >> k;
for(int i=1;i<=n;i++)
cin >> a[i],a[i]++;
m = 1000001; SA();
k--;//维护k个元素的队列最小值
int ans = 0;
for(int i=2;i<=n;i++)
{
while( head<=tail&&height[id[tail]]>=height[i] ) tail--;
id[++tail] = i;
while( id[head]<=i-k ) head++;
if( i>=k+1 ) ans = max( ans,height[id[head]] );
}
cout << ans;
}