[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;
}
posted @ 2020-05-22 20:13  xryjr233  阅读(258)  评论(1编辑  收藏  举报