zoukankan      html  css  js  c++  java
  • [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;
    }
    

  • 相关阅读:
    TensorFlow Executor解析
    面试复习
    [洛谷]P1880 石子合并问题
    [西建大ACM协会]OJ平台如何使用
    [ACM] 相关OJ及在线运行代码网站
    [MySQL] Win10 安装MySQL5.7.27
    [PTA] PAT(A) 1012 The Best Rank (25 分)
    [PTA] PAT(A) 1011 World Cup Betting (20 分)
    [PTA] PAT(A) 1010 Radix (25 分)
    [PTA] PAT(A) 1009 Product of Polynomials (25 分)
  • 原文地址:https://www.cnblogs.com/Troverld/p/14605418.html
Copyright © 2011-2022 走看看