题解 P5341 【[TJOI2019]甲苯先生和大中锋的字符串】
Update1:修了一点小锅
[TJOI2019]甲苯先生和大中锋的字符串
提供一个
题面有点绕,大意是找出仅出现
考虑如何求出子串集合,先建好 height 数组,问题可以转化为在 height 数组上找一个长度为
设区间
根据 height 数组的定义,有
接下来考虑
感性证明一下,若
于是我们可以对 height 数组做一个类似于滑动窗口的东西,用差分数组维护每个区间对应的
既然你能找到这题,我相信你能瞬间做出来的。
#include<bits/stdc++.h>
typedef long long LL;
typedef long double LD;
using namespace std;
const LL N=100010,INF=0x3f3f3f3f;
inline LL Max(LL x,LL y){return x>y?x:y;}
inline LL Min(LL x,LL y){return x<y?x:y;}
inline void Swap(LL &x,LL &y){x^=y^=x^=y;}
LL T,n,m,k,cf[N];
char s[N];
namespace SA{
LL sa[N],height[N],c[N],x[N],y[N],rk[N];
void get_sa(){
for(LL i=1;i<=m;i++)c[i]=0;
for(LL i=1;i<=n;i++)c[x[i]=s[i]]++;
for(LL i=2;i<=m;i++)c[i]+=c[i-1];
for(LL i=n;i>=1;i--)sa[c[x[i]]--]=i;
for(LL w=1;w<=n;w<<=1){
LL num=0;
for(LL i=n-w+1;i<=n;i++)y[++num]=i;
for(LL i=1;i<=n;i++)
if(sa[i]>w)y[++num]=sa[i]-w;
for(LL i=1;i<=m;i++)c[i]=0;
for(LL i=1;i<=n;i++)c[x[i]]++;
for(LL i=2;i<=m;i++)c[i]+=c[i-1];
for(LL i=n;i>=1;i--)sa[c[x[y[i]]]--]=y[i],y[i]=0;
std::swap(x,y);
x[sa[1]]=num=1;
for(LL i=2;i<=n;i++)
x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+w]==y[sa[i-1]+w])?num:++num;
if(num==n)break;
m=num;
}
}
void get_height(){
for(LL i=1;i<=n;i++)rk[sa[i]]=i;
for(LL i=1,k=0;i<=n;i++){
if(rk[i]==1)continue;
if(k)k--;
LL j=sa[rk[i]-1];
while(i+k<=n&&j+k<=n&&s[i+k]==s[j+k])k++;
height[rk[i]]=k;
}
}
}
struct SegmentTree{
LL l,r;
LL mn;
#define l(x) tree[x].l
#define r(x) tree[x].r
#define mn(x) tree[x].mn
}tree[N<<2];
void Pushup(LL x){
mn(x)=Min(mn(x<<1),mn(x<<1|1));
}
void Build(LL x,LL l,LL r){
l(x)=l,r(x)=r;mn(x)=0;
if(l==r){mn(x)=SA::height[l];return;}
LL mid=(l+r)>>1;
Build(x<<1,l,mid);
Build(x<<1|1,mid+1,r);
Pushup(x);
}
LL Query(LL x,LL L,LL R){
LL l=l(x),r=r(x);
if(L<=l&&r<=R)return mn(x);
LL mid=(l+r)>>1;
LL val=INF;
if(L<=mid)val=Min(val,Query(x<<1,L,R));
if(R>mid)val=Min(val,Query(x<<1|1,L,R));
return val;
}
int main(){
scanf("%lld",&T);
while(T--){
scanf("%s%lld",s+1,&k);
n=strlen(s+1);m=122;
memset(cf,0,sizeof(cf));
SA::height[n+1]=0;
SA::get_sa();
SA::get_height();
Build(1,1,n);
for(LL i=1;i+k-1<=n;i++){
LL l=i,r=i+k-1,L,R;
if(l+1>r)R=n-SA::sa[r]+1;
else R=Query(1,l+1,r);
L=Max(SA::height[l],SA::height[r+1])+1;
if(L<=R)++cf[L],--cf[R+1];
}
LL ans=-1,maxv=1;
for(LL i=1;i<=n;i++){
cf[i]+=cf[i-1];
if(cf[i]>=maxv)ans=i,maxv=cf[i];
}
printf("%lld\n",ans);
}
return 0;
}