• [ICPC2019 WF]First of Her Name


    X.[ICPC2019 WF]First of Her Name

    这题的一种解法是把所有东西(名字串和翻转的询问串)建出一棵树来,然后跑树上后缀排序,用二分+哈希求出\(height\)数组,然后使用单调栈找出每个位置最多能够向左向右延伸多远。但是按照某人的说法出题人好像出了卡此算法的数据,所以最后WA掉了。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int N=2001000;
    const int NN=2000000;
    typedef unsigned long long ull;
    ull sd1=31,sd2=43,iv1=17256631552825064415ull,iv2=9437869060967677571ull;
    ull pov1[N],pov2[N],inv1[N],inv2[N];
    struct HASH{
    	ull val1,val2;
    	int len;
    	HASH(){val1=val2=0ull,len=0;}
    	HASH(int ip){val1=val2=ip,len=1;}
    	friend HASH operator +(const HASH &x,const HASH &y){
    		HASH z;
    		z.val1=x.val1*pov1[y.len]+y.val1;
    		z.val2=x.val2*pov2[y.len]+y.val2;
    		z.len=x.len+y.len;
    		return z;
    	}
    	friend HASH operator -(const HASH &x,const HASH &y){
    		HASH z;
    		z.val1=(x.val1-y.val1)*inv1[y.len];
    		z.val2=(x.val2-y.val2)*inv2[y.len];
    		z.len=x.len-y.len;
    		return z;
    	}
    	friend bool operator ==(const HASH &x,const HASH &y){
    		if(x.len!=y.len)return false;
    		if(x.val1!=y.val1)return false;
    		if(x.val2!=y.val2)return false;
    		return true;
    	}
    }hs[N];
    int n,anc[N][21],sa[N],rk[N],x[N],y[N],buc[N],m,ht[N],q,dep[N];
    char s[N],t[N];
    int HASH_LCP(int u,int v){
    	int len=0;
    	for(int i=20;i>=0;i--)if((dep[u]>=(1<<i))&&(dep[v]>=(1<<i))&&(hs[u]-hs[anc[u][i]])==(hs[v]-hs[anc[v][i]]))len+=(1<<i),u=anc[u][i],v=anc[v][i];
    	return len;
    }
    bool mat(int i,int j,int k){
    	if(y[i]!=y[j])return false;
    	if(!anc[i][k]&&!anc[j][k])return true;
    	if(anc[i][k]&&anc[j][k])return y[anc[i][k]]==y[anc[j][k]];
    	return false;
    }
    void SA(){
    	m=26;
    	for(int i=1;i<=n;i++)++buc[x[i]=s[i]];
    	for(int i=1;i<=m;i++)buc[i]+=buc[i-1];
    	for(int i=n;i;i--)sa[buc[x[i]]--]=i;
    //	for(int i=1;i<=n;i++)printf("%d ",sa[i]);puts("");
    	for(int k=0;k<=20;k++){
    		int num=0;
    		for(int i=1;i<=m;i++)buc[i]=0;
    		for(int i=1;i<=n;i++)if(!anc[i][k])y[++num]=i;else ++buc[x[anc[i][k]]];
    		for(int i=1;i<=m;i++)buc[i]+=buc[i-1];
    		for(int i=n;i;i--)if(anc[i][k])y[num+buc[x[anc[i][k]]]--]=i;
    		
    //		printf("Y:");for(int i=1;i<=n;i++)printf("%d ",y[i]);puts("");
    		
    		for(int i=1;i<=m;i++)buc[i]=0;
    		for(int i=1;i<=n;i++)++buc[x[y[i]]];
    		for(int i=1;i<=m;i++)buc[i]+=buc[i-1];
    		for(int i=n;i;i--)sa[buc[x[y[i]]]--]=y[i];
    		
    		swap(x,y);
    		x[sa[1]]=num=1;
    		for(int i=2;i<=n;i++)x[sa[i]]=mat(sa[i],sa[i-1],k)?num:++num;
    		m=num;
    		
    //		printf("S:");for(int i=1;i<=n;i++)printf("%d ",sa[i]);puts("");
    //		printf("X:");for(int i=1;i<=n;i++)printf("%d ",x[i]);puts("");
    //		puts("");
    	}
    	for(int i=1;i<=n;i++)rk[sa[i]]=i;
    	for(int i=1;i<n;i++)ht[i]=HASH_LCP(sa[i],sa[i+1]);
    }
    int L[N],R[N],pos[N],stk[N],tp,sum[N],lim;
    int main(){
    	pov1[0]=pov2[0]=inv1[0]=inv2[0]=1;
    	for(int i=1;i<=NN;i++)pov1[i]=pov1[i-1]*sd1,pov2[i]=pov2[i-1]*sd2,inv1[i]=inv1[i-1]*iv1,inv2[i]=inv2[i-1]*iv2;
    	scanf("%d%d",&n,&q),lim=n;
    	for(int i=1,fa;i<=n;i++)scanf("%s%d",t,&anc[i][0]),s[i]=t[0]-'A'+1,hs[i]=HASH(s[i])+hs[anc[i][0]],dep[i]=dep[anc[i][0]]+1;
    	for(int i=1,len;i<=q;i++){
    		scanf("%s",t+1),len=strlen(t+1),reverse(t+1,t+len+1);
    		for(int j=1;j<=len;j++)s[n+j]=t[j]-'A'+1,dep[n+j]=j;
    		for(int j=2;j<=len;j++)anc[n+j][0]=n+j-1;
    		for(int j=1;j<=len;j++)hs[n+j]=HASH(s[n+j])+hs[anc[n+j][0]];
    		n+=len;
    		pos[i]=n;
    	}
    //	for(int i=1;i<=n;i++)printf("%d:%d ",i,dep[i]);puts("");
    	for(int j=1;j<=20;j++)for(int i=1;i<=n;i++)anc[i][j]=anc[anc[i][j-1]][j-1];
    	SA();
    //	for(int i=1;i<=n;i++)printf("%d:%2d ",i,sa[i]);puts("");
    //	for(int i=1;i<=n;i++)printf("%d:%2d ",i,ht[i]);puts("");
    //	for(int i=1;i<=n;i++)printf("%d:%2d ",i,dep[sa[i]]);puts("");
    	tp=0;
    	for(int i=1;i<=n;i++){
    		while(tp&&dep[sa[stk[tp]]]>ht[i])R[stk[tp]]=i,tp--;
    		if(dep[sa[i]]>ht[i])R[i]=i;
    		else stk[++tp]=i;
    	}
    	tp=0;
    	for(int i=n;i;i--){
    		while(tp&&dep[sa[stk[tp]]]>ht[i-1])L[stk[tp]]=i,tp--;
    		if(dep[sa[i]]>ht[i-1])L[i]=i;
    		else stk[++tp]=i;
    	}
    //	for(int i=1;i<=n;i++)printf("%d:(%d,%d)",i,L[i],R[i]);
    	for(int i=1;i<=n;i++)sum[i]=sum[i-1]+(sa[i]<=lim);
    //	for(int i=1;i<=n;i++)printf("%d %d %d\n",anc[i][0],i,s[i]);
    	for(int i=1;i<=q;i++){
    		pos[i]=rk[pos[i]];
    		printf("%d\n",sum[R[pos[i]]]-sum[L[pos[i]]-1]);
    	}
    	return 0;
    }
    

    还有一种方法则和蔼得多,就是我们这里讲解的AC自动机。

    我们仍将名字串和翻转的询问串建出树来,不过这次是字典树。然后建出AC自动机。

    考虑\(fail\)树。一个节点在\(fail\)树上的子树中,包含了所有有它作为后缀的串。所以我们只需要求出每个询问在\(fail\)树中的子树中有多少个来自原串的节点即可。

    时间复杂度\(\sum|S|\times|\alpha|\),其中\(|\alpha|\)是字符集大小。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    int n,m,cnt=1,res[1001000];
    struct AC_Automaton{
    	int ch[26],fail,sz;
    	vector<int>v,ed;
    }t[2001000];
    char s[1001000];
    void Ins(int id,int len){
    	int x=1;
    	for(int i=0;i<len;i++){
    		if(!t[x].ch[s[i]-'A'])t[x].ch[s[i]-'A']=++cnt;
    		x=t[x].ch[s[i]-'A'];
    	}
    	t[x].ed.push_back(id);
    }
    queue<int>q;
    void build(){
    	for(int i=0;i<26;i++){
    		if(t[1].ch[i])t[t[1].ch[i]].fail=1,q.push(t[1].ch[i]),t[1].v.push_back(t[1].ch[i]);
    		else t[1].ch[i]=1;
    	}
    	while(!q.empty()){
    		int x=q.front();q.pop();
    		for(int i=0;i<26;i++){
    			if(t[x].ch[i])t[t[x].ch[i]].fail=t[t[x].fail].ch[i],q.push(t[x].ch[i]),t[t[t[x].fail].ch[i]].v.push_back(t[x].ch[i]);
    			else t[x].ch[i]=t[t[x].fail].ch[i];
    		}
    	}
    }
    void dfs(int x){
    	for(auto y:t[x].v)dfs(y),t[x].sz+=t[y].sz;
    	for(auto i:t[x].ed)res[i]=t[x].sz;
    }
    int main(){
    	scanf("%d%d",&n,&m);
    	for(int i=2,x;i<=n+1;i++)scanf("%s%d",s,&x),t[x+1].ch[s[0]-'A']=++cnt,t[cnt].sz=1;
    	for(int i=1,len;i<=m;i++)scanf("%s",s),len=strlen(s),reverse(s,s+len),Ins(i,len);
    	build(),dfs(1);
    //	for(int i=1;i<=cnt;i++)printf("%d<-%d %d\n",i,t[i].fail,t[i].sz);
    	for(int i=1;i<=m;i++)printf("%d\n",res[i]);
    	return 0;
    }
    
  • 相关阅读:
    Poj1163 The Triangle(动态规划求最大权值的路径)
    Poj1258_Agri-Net(最小生成树)
    Poj1258_Agri-Net(最小生成树)
    Poj1218_THE DRUNK JAILER(水题)
    Poj1218_THE DRUNK JAILER(水题)
    Poj1298_The Hardest Problem Ever(水题)
    Poj1298_The Hardest Problem Ever(水题)
    Poj1012_Joseph
    Poj1012_Joseph
    Poj_1008--Maya Calendar
  • 原文地址:https://www.cnblogs.com/Troverld/p/14596706.html
Copyright © 2020-2023  润新知