[2020.5.22]UOJ523 【美团杯2020】半前缀计数
考虑将某一个半前缀和原串匹配尽可能长的前缀。
于是我们可以枚举前缀匹配的长度,那么对于某一个前缀\(s_{1\dots i}\),半前缀数量就是后缀\(s_{i+1\dots n}\)中,不以字符\(s_{i+1}\)开头的本质不同字串数量。
我们从\(n\)到\(1\)枚举前缀,每次加入一一个后缀,维护每个后缀的height(只考虑已加入的后缀),所有后缀的height和,以及每个一字母开头的后缀长度和和height和。
那么对于一个前缀\(s_{1\dots i}\),答案就是所有后缀长度和-(height和-所有以\(s_{i+1}\)开头的后缀的height和)-以\(s_{i+1}\)开头的后缀长度和。
写个SA,用树状数组计算每一个后缀被加入时,已经加入的且与该后缀在\(sa\)上相邻的两个后缀,当加入一个后缀时,就可以\(O(1)\)更新height了。
时间复杂度\(O(n\log n)\)。
虽然SA常数很大,但是本题还是在1s内跑过去了?
code:
#include<bits/stdc++.h>
#define ci const int&
using namespace std;
int n,M=27,tmp,rnk[1000010],sa[1000010],lr[1000010],ba[1000010],he[1000010],lg[1000010],mn[20][1000010],lm,l[30],r[30],m[1000010],li[1000010],ri[1000010],nh[1000010];
long long ans,tl,sh,sum[30],ts[30];
char s[1000010];
void Sort(){
for(int i=1;i<=M;++i)ba[i]=0;
for(int i=1;i<=n;++i)++ba[rnk[i]];
for(int i=1;i<=M;++i)ba[i]+=ba[i-1];
for(int i=n;i>=1;--i)sa[ba[rnk[lr[i]]]--]=lr[i];
}
void SA(){
for(int i=1;i<=n;++i)rnk[i]=s[i]-'a'+1,lr[i]=i;
Sort();
for(int i=1;i<n;i<<=1,M=tmp){
tmp=0;
for(int j=n-i+1;j<=n;++j)lr[++tmp]=j;
for(int j=1;j<=n;++j)if(sa[j]>i)lr[++tmp]=sa[j]-i;
Sort(),tmp=0;
for(int j=1;j<=n;++j)lr[j]=rnk[j];
for(int j=1;j<=n;++j)rnk[sa[j]]=(lr[sa[j]]==lr[sa[j-1]]&&lr[sa[j]+i]==lr[sa[j-1]+i]?tmp:++tmp);
}
}
void Getheight(){
for(int i=1;i<=n;++i){
tmp=max(0,he[rnk[i-1]]-1);
while(i+tmp<=n&&sa[rnk[i]+1]+tmp<=n&&s[i+tmp]==s[sa[rnk[i]+1]+tmp])++tmp;
he[rnk[i]]=mn[0][rnk[i]]=tmp;
}
}
void Ins(int x,ci v){
while(x<=n)m[x]=max(m[x],v),x+=(x&-x);
}
int Que(int x){
int ret=0;
while(x)ret=max(ret,m[x]),x-=(x&-x);
return ret;
}
void _Ins(int x,ci v){
while(x<=n)m[x]=min(m[x],v),x+=(x&-x);
}
int _Que(int x){
int ret=1e9;
while(x)ret=min(ret,m[x]),x-=(x&-x);
return ret;
}
int Min(ci ql,ci qr){
int k=lg[qr-ql+1];
return min(mn[k][ql],mn[k][qr-(1<<k)+1]);
}
int main(){
scanf("%s",s+1),n=strlen(s+1);
SA(),Getheight();
for(int i=2;i<=n;++i)lg[i]=lg[i>>1]+1;
for(lm=1;(1<<lm)<=n;++lm)for(int i=1;i+(1<<lm)-1<=n;++i)mn[lm][i]=min(mn[lm-1][i],mn[lm-1][i+(1<<lm-1)]);
tmp=0;
for(int i=1;i<=n;++i)if(s[sa[i]]!=s[sa[i+1]])l[s[sa[i]]-'a']=tmp+1,tmp=r[s[sa[i]]-'a']=i;
for(int i=1;i<=n;++i)li[i]=Que(n-sa[i]+1),Ins(n-sa[i]+1,i);
for(int i=1;i<=n;++i)m[i]=1e9;
for(int i=n;i>=1;--i)ri[i]=_Que(n-sa[i]+1),_Ins(n-sa[i]+1,i);
for(int i=n;i>=0;--i){
if(i!=n)ans+=tl-(sh-sum[s[i+1]-'a'])-ts[s[i+1]-'a']+1;
else ++ans;
if(!i)continue;
tl+=n-i+1,ts[s[i]-'a']+=n-i+1;
tmp=li[rnk[i]];
if(tmp)sh-=nh[tmp],sum[s[sa[tmp]]-'a']-=nh[tmp],nh[tmp]=Min(tmp,rnk[i]-1),sh+=nh[tmp],sum[s[sa[tmp]]-'a']+=nh[tmp];
tmp=ri[rnk[i]];
if(tmp<=n)nh[rnk[i]]=Min(rnk[i],tmp-1),sh+=nh[rnk[i]],sum[s[i]-'a']+=nh[rnk[i]];
}
printf("%lld",ans);
return 0;
}