zoukankan      html  css  js  c++  java
  • [2020.5.22]UOJ523 【美团杯2020】半前缀计数

    考虑将某一个半前缀和原串匹配尽可能长的前缀。

    于是我们可以枚举前缀匹配的长度,那么对于某一个前缀(s_{1dots i}),半前缀数量就是后缀(s_{i+1dots n})中,不以字符(s_{i+1})开头的本质不同字串数量。

    我们从(n)(1)枚举前缀,每次加入一一个后缀,维护每个后缀的height(只考虑已加入的后缀),所有后缀的height和,以及每个一字母开头的后缀长度和和height和。

    那么对于一个前缀(s_{1dots i}),答案就是所有后缀长度和-(height和-所有以(s_{i+1})开头的后缀的height和)-以(s_{i+1})开头的后缀长度和。

    写个SA,用树状数组计算每一个后缀被加入时,已经加入的且与该后缀在(sa)上相邻的两个后缀,当加入一个后缀时,就可以(O(1))更新height了。

    时间复杂度(O(nlog 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;
    }
    
  • 相关阅读:
    算法笔记 --- Selection Sort
    算法笔记 --- Radix Sort
    算法笔记 --- Quick Sort
    算法笔记 --- Merge Sort
    算法笔记 --- Insertion Sort
    算法笔记 --- Heap Sort
    算法笔记 --- Counting Sort
    算法笔记 --- Bubble Sort
    算法笔记 --- Tree Travers
    javaweb_JDBC
  • 原文地址:https://www.cnblogs.com/xryjr233/p/UOJ523.html
Copyright © 2011-2022 走看看