权限题。
题意:
求一个字符串中出现超过k次的最长子串,可重叠
(n<=20000)
Solution:
先用后缀数组跑出height
然后我们知道一个性质:任意两个后缀的最长公共前缀就是它们之间所有height取min
那么这个问题就相当于枚举每个长为k-1的区间,在区间内部求min,每个区间求max,倍增解决即可
代码:
#include<cstdio>
#include<iostream>
using namespace std;
const int N=20010;
int m,n,kk,a[N],p;
int tax[1000000],SA[N],tp[N],rk[N],height[N][21];
int num[N],mi[20];
void Jsort()
{
for (int i=0;i<=m;i++) tax[i]=0;
for (int i=1;i<=n;i++) tax[rk[tp[i]]]++;
for (int i=1;i<=m;i++) tax[i]+=tax[i-1];
for (int i=n;i>=1;i--) SA[tax[rk[tp[i]]]--]=tp[i];
}
bool cmp(int tp[],int i,int j,int w)
{
return (tp[i]==tp[j]&&tp[i+w]==tp[j+w]);
}
int main()
{
scanf("%d%d",&n,&kk);
for (int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
rk[i]=a[i],tp[i]=i;m=max(a[i],m);
}
Jsort();
for (int w=1;p<n;w+=w,m=p)
{
p=0;
for (int i=n-w+1;i<=n;i++) tp[++p]=i;
for (int i=1;i<=n;i++) if (SA[i]>w) tp[++p]=SA[i]-w;
Jsort();swap(rk,tp);
p=1;rk[SA[1]]=p;
for (int i=2;i<=n;i++)
if (cmp(tp,SA[i],SA[i-1],w)) rk[SA[i]]=p;else rk[SA[i]]=++p;
}
int k=0;
for (int i=1;i<=n;i++)
{
if (rk[i]==1) {height[1][0]=0;continue;}
if (k) k--;
int j=SA[rk[i]-1];
while (a[i+k]==a[j+k]&&i+k<=n&&j+k<=n) k++;
height[rk[i]][0]=k;
//cout<<rk[i]<<" "<<k<<endl;
};
mi[0]=1;for (int i=1;i<=20;i++) mi[i]=mi[i-1]*2;
for (int i=1;i<=20;i++)
for (int j=2;j<=n;j++)
{
height[j][i]=min(height[j][i-1],height[min(j+mi[i-1],n)][i-1]);
//cout<<j<<" "<<i<<" "<<height[j][i]<<endl;
}
kk--;
int ans=0;
for (int j=2;j<=n-kk+1;j++)
{
int nw=kk,pos=j,tot=n;
for (int i=20;i>=0;i--)
if (nw>=mi[i]) tot=min(tot,height[min(n,pos)][i]),nw-=mi[i],pos+=mi[i];
ans=max(ans,tot);
}
printf("%d",ans);
}