zoukankan      html  css  js  c++  java
  • BZOJ 2905: 背单词 AC自动机+fail树+dfs序+线段树

    Description

    给定一张包含N个单词的表,每个单词有个价值W。要求从中选出一个子序列使得其
    中的每个单词是后一个单词的子串,最大化子序列中W的和。 
     

    Input

    第一行一个整数TEST,表示数据组数。 
    接下来TEST组数据,每组数据第一行为一个整数N。 
    接下来N行,每行为一个字符串和一个整数W。 

    Output

    TEST行,每行一个整数,表示W的和的最大值。 
     
     
    数据规模 
    设字符串的总长度为Len 
    30.的数据满足,TEST≤5,N≤500,Len≤10^4 
    100.的数据满足,TEST≤10,N≤20000,Len≤3*10^5

    题解: 

    感觉很多 AC 自动机的套路都是将 $trie$ 和 $fail$ 树结合,然后在 $fail$ 树上维护一些东西.
    对于本题,首先可以排除掉那些权值小于等于 $0$ 的字符串(出题人是认真的吗?)
    构建出来所有单词的 $fail$ 树后,依次枚举每一个字符串,记该字符串在 $trie$ 树中终止节点为 $end(i)$.
    那么,如果该单词包含了之前的一个单词,那么后缀就可能来自 $trie$ 树中根节点到该点.
    依次枚举这条路径上的点,查询这条路径上在节点在 $fail$ 树对应的 $dfs$ 序上查询一下最大值.
    而所有这些值的极大值就是 $i$ 为最后一个串的答案.
    考虑 $i$ 可以对后面哪些串有贡献:就是 $fail$ 树中 $i$ 子树内的所有点,这个用线段树维护 $dfs$ 序即可.

    #include <cstdio>
    #include <queue>  
    #include <map>   
    #include <algorithm>  
    #include <cstring> 
    #define N 300002 
    #define setIO(s) freopen(s".in","r",stdin) 
    using namespace std; 
    int T,cas;  
    struct Seg { 
    	#define lson (now<<1|1)
    	#define rson (now<<1)   
    	struct Node { 
    		int tag; 
    	}t[N<<2];  
    	void update(int l,int r,int now,int L,int R,int v) { 
    		if(l>=L&&r<=R) {
    			t[now].tag=max(t[now].tag, v);    
    			return; 
    		}  
    		int mid=(l+r)>>1; 
    		if(L<=mid) update(l,mid,lson,L,R,v); 
    		if(R>mid) update(mid+1,r,rson,L,R,v);     
    	}
    	int query(int l,int r,int now,int p,int pre) {
    		pre=max(pre, t[now].tag);     
    		if(l==r) return pre;        
    		int mid=(l+r)>>1;    
    		if(p<=mid) return query(l,mid,lson,p,pre); 
    		else return query(mid+1,r,rson,p,pre);     
    	}   
    	#undef lson 
    	#undef rson
    }seg; 
    struct Node { 
    	int f, ch[27];     
    }t[N]; 
    queue<int>q; 
    char str[N]; 
    int n,tot,tim,edges,w[N],endpos[N],hd[N],nex[N],to[N],dfn[N],size[N]; 
    void addedge(int u,int v) {
    	nex[++edges]=hd[u],hd[u]=edges,to[edges]=v; 
    }
    void dfs(int u) { 
    	dfn[u]=++tim,size[u]=1; 
    	for(int i=hd[u];i;i=nex[i]) dfs(to[i]), size[u]+=size[to[i]];      
    }
    int insert() {
    	int len=strlen(str+1),i,rt=0; 
    	for(i=1;i<=len;++i) { 
    		if(!t[rt].ch[str[i]-'a']) t[rt].ch[str[i]-'a']=++tot;  
    		rt=t[rt].ch[str[i]-'a'];      
    	}  
    	return rt; 
    } 
    void build() { 
    	int i,j; 
    	for(i=0;i<27;++i) if(t[0].ch[i]) q.push(t[0].ch[i]); 
    	while(!q.empty()) {
    		int u=q.front();q.pop(); 
    		for(i=0;i<27;++i) {
    			int p=t[u].ch[i]; 
    			if(!p) {
    				t[u].ch[i]=t[t[u].f].ch[i];  
    				continue; 
    			} 
    			t[p].f=t[t[u].f].ch[i];   
    			q.push(p);   
    		}
    	}      
    }
    void solve() {
    	int i,j;  
    	scanf("%d",&n);     
    	for(i=1;i<=n;++i) { 
    		scanf("%s%d",str+1,&w[i]); 
    		if(w[i]>0) endpos[i]=insert(); 
    	}     
    	build(); 
    	for(i=1;i<=tot;++i) 
    		addedge(t[i].f,i); 
    	dfs(0);         
    	int answer=0; 
        for(i=1;i<=n;++i) { 
        	if(w[i]<=0) continue;       
        	int p=endpos[i],re=0; 
        	while(p) re=max(re, seg.query(1,tim,1,dfn[p],0)), p=t[p].f;  
        	re+=w[i];  
        	answer=max(answer, re);     
        	seg.update(1,tim,1,dfn[endpos[i]],dfn[endpos[i]]+size[endpos[i]]-1,re);        
        }   
        printf("%d
    ",answer);        
        memset(hd,0,sizeof(hd)), memset(endpos,0,sizeof(endpos)), memset(t,0,sizeof(t)); 
        tot=tim=edges=0;  
        memset(seg.t,0,sizeof(seg.t));      
    }
    int main() { 
    	// setIO("input");  
    	scanf("%d",&T); 
    	for(cas=1;cas<=T;++cas) solve(); 
    	return 0;    
    }
    

      

  • 相关阅读:
    zap日志库
    Go Logger库基本使用
    golang中的MySQL增删改查
    golang链接mysql
    文件上傳
    參數綁定:
    gin獲取path參數
    gin獲取form提交的表單數據
    獲取query string
    返回json
  • 原文地址:https://www.cnblogs.com/guangheli/p/11434413.html
Copyright © 2011-2022 走看看