[BZOJ3277]串/CF204E Little Elephant and Strings

XXVIII.[BZOJ3277]串/CF204E Little Elephant and Strings

这两题是重题,代码改都不改交上去就能A,故放在一起讲。

网上的大多数SA题解都是\(O(n\log^2n)\)或\(O(n\log n)\)的复杂度,太令人不爽了。因此,这里有一种复杂度\(O(n)\)的SA题解。

一看到\(O(n)\)的SA,就应该猜出我们的目标是什么了——笛卡尔树。但是这里我们就要判断笛卡尔树的区间内部出现了多少条不同的原串。在之前的X.[SCOI2012]喵星球上的点名中,我们使用了\(O(n\log n)\)的二维数点来解决此类问题。但是这题的\(K\)是固定的,所以有没有更优美的方法呢?

有的!我们可以用two-pointers求出对于每个位置\(x\),它左侧最右边的一个位置\(lft_x\)且使得\([lft_x,x]\)中出现了恰好\(K\)个不同的原串。则我们如果要再来判断区间\([L,R]\)是否合法的话,只需要判断是否有\(L\leq lft_R\)即可。

使用单调栈建笛卡尔树+two-pointers求\(lft\)+差分计算贡献+DC3后缀排序,即可将复杂度压至\(O(n)\)。

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=200100;
int n,m,id[N],A,B,len[N];
int x[N],y[N],buc[N],sa[N],ht[N],rk[N],s[N];
char str[N];
bool mat(int a,int b,int k){
	if(y[a]!=y[b])return false;
	if((a+k<n)^(b+k<n))return false;
	if((a+k<n)&&(b+k<n))return y[a+k]==y[b+k];
	return true;
}
void SA(){
	for(int i=0;i<n;i++)buc[x[i]=s[i]]++;
	for(int i=1;i<=m;i++)buc[i]+=buc[i-1];
	for(int i=n-1;i>=0;i--)sa[--buc[x[i]]]=i;
	for(int k=1;k<n;k<<=1){
		int num=0;
		for(int i=n-k;i<n;i++)y[num++]=i;
		for(int i=0;i<n;i++)if(sa[i]>=k)y[num++]=sa[i]-k;
		for(int i=0;i<=m;i++)buc[i]=0;
		for(int i=0;i<n;i++)buc[x[y[i]]]++;
		for(int i=1;i<=m;i++)buc[i]+=buc[i-1];
		for(int i=n-1;i>=0;i--)sa[--buc[x[y[i]]]]=y[i];
		swap(x,y);
		x[sa[0]]=num=0;
		for(int i=1;i<n;i++)x[sa[i]]=mat(sa[i],sa[i-1],k)?num:++num;
		if(num>=n-1)break;
		m=num;
	}
	for(int i=0;i<n;i++)rk[sa[i]]=i;
	for(int i=0,k=0;i<n;i++){
		if(!rk[i])continue;
		if(k)k--;
		int j=sa[rk[i]-1];
		while(i+k<n&&j+k<n&&s[i+k]==s[j+k])k++;
		ht[rk[i]]=k;
	}
}
int lft[N],occ[N];
int L[N],R[N],lson[N],rson[N],stk[N],tp;
int diff[N];
ll res[N];
void solve(int x,int las){
//	printf("%d:[%d,%d]:%d-%d\n",x,L[x],R[x],las,ht[x]);
	int tmp;
	if(L[x]<=lft[R[x]-1])tmp=ht[x]-las,diff[L[x]]+=tmp,diff[R[x]]-=tmp/*,printf("A:%d:[%d,%d]:%d\n",x,L[x],R[x]-1,tmp)*/;
	if(lson[x])solve(lson[x],ht[x]);
	else if(B==1)tmp=len[sa[L[x]]]-ht[x],diff[L[x]]+=tmp,diff[L[x]+1]-=tmp/*,printf("B:%d:[%d,%d]:%d\n",x,L[x],L[x],tmp)*/;
	if(rson[x])solve(rson[x],ht[x]);
	else if(B==1)tmp=len[sa[R[x]-1]]-ht[x],diff[R[x]-1]+=tmp,diff[R[x]]-=tmp/*,printf("C:%d:[%d,%d]:%d\n",x,R[x]-1,R[x]-1,tmp)*/;
}
int main(){
	scanf("%d%d",&A,&B);
	for(int i=1;i<=A;i++){
		scanf("%s",str),m=strlen(str);
		for(int j=0;j<m;j++)s[n]=str[j]-'a'+1,id[n]=i,len[n]=m-j,n++;
		s[n++]=i+26;
	}
//	for(int i=0;i<n;i++)printf("%2d ",s[i]);puts("");
	m=A+26;
	SA();
//	for(int i=0;i<n;i++)printf("%2d ",sa[i]);puts("");
//	for(int i=0;i<n;i++)printf("%d ",id[sa[i]]);puts("");
//	for(int i=0;i<n;i++)printf("%d ",ht[i]);puts("");
	for(int i=0,j=0,k=0;j<n;j++){
		if(id[sa[j]])k+=!occ[id[sa[j]]]++;
		for(;k>=B;i++)if(id[sa[i]])k-=!--occ[id[sa[i]]];
		lft[j]=i-1;
	}
//	for(int i=0;i<n;i++)printf("%d ",lft[i]);puts("");
	for(int i=1;i<n;i++){
		while(tp&&ht[stk[tp]]>ht[i])lson[i]=stk[tp],R[stk[tp]]=i,tp--;
		if(tp)rson[stk[tp]]=i;
		L[i]=stk[tp],stk[++tp]=i;
	}
	while(tp)R[stk[tp--]]=n;
//	for(int i=1;i<n;i++)printf("[%d,%d]",L[i],R[i]);puts("");
	solve(stk[1],0);
	for(int i=1;i<n;i++)diff[i]+=diff[i-1];
//	for(int i=0;i<n;i++)printf("%d ",diff[i]);puts("");
	for(int i=0;i<n;i++)if(id[sa[i]])res[id[sa[i]]]+=diff[i];
	for(int i=1;i<=A;i++)printf("%lld ",res[i]);
	return 0;
}

上一篇:linux centos 7 网卡突然不能上网异常解决


下一篇:LVS+DR+apache+keepalived负载均衡