传送门

原串的所有子串都可以看成某个后缀的前缀

我们考虑建立后缀数组

那么两个相同的子串,所属的后缀在 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 k1个元素的单调队列最小值即可

取所有区间的最大值就是答案

#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;
}