• [GDOI2017]微信(广义SAM+状态压缩)


    [GDOI2017]微信(广义SAM+状态压缩)

    题面

    题面过长,略

    分析

    先把n个串合并建出Trie. 由于n很小,对于Trie的每个节点,我们用状压记录这个节点代表的子串来自哪些串。然后BFS这个Trie,建出广义SAM.对于SAM中新建的每个节点,同样维护这个子串来自哪些串,构建的时候把它赋值为原Trie上的状态(复制出来的节点为0).然后在parent树上,把子树中的状态并上来。

    接着设(f[S])表示询问的串状态为(S)的答案。设后缀自动机上每个点的状态为(S_x),我们初始化(f[S_x]=len(x)).那么由于x代表在(S_x)中的每个串都存在,那么在(S_x)的子集中的每个串都存在。我们可以用(f[S_x])去更新(f[S_x-{ i}]),即:

    [f[S-{ i}]=min(f[S-{ i}],f[S]) ]

    对于询问,直接输出对应的(f)即可。时间复杂度(O(sum |S|+2^n))

    代码

    #include<iostream>
    #include<cstdio>
    #include<cstring> 
    #include<queue>
    #define maxn 2000000
    #define maxc 26
    #define maxs (1<<20)
    using namespace std;
    int n,m;
    char in[maxn+5];
    
    struct EXSAM{
    #define len(x) t[x].len
    #define link(x) t[x].link 
    	struct node {
    		int ch[maxc];
    		int link;
    		int len;
    		int sta;
    	} t[maxn*2+5];
    	const int root=1;
    	int ptr=1;
    	int extend(int last,int c,int sta) {
    		int p=last,cur=++ptr;
    		len(cur)=len(p)+1;
    		t[cur].sta=sta;
    		while(p&&t[p].ch[c]==0) {
    			t[p].ch[c]=cur;
    			p=link(p);
    		}
    		if(p==0) link(cur)=root;
    		else {
    			int q=t[p].ch[c];
    			if(len(p)+1==len(q)) link(cur)=q;
    			else {
    				int clo=++ptr;
    				len(clo)=len(p)+1;
    				for(int i=0; i<maxc; i++) t[clo].ch[i]=t[q].ch[i];
    				link(clo)=link(q);
    				link(q)=link(cur)=clo;
    				while(p&&t[p].ch[c]==q) {
    					t[p].ch[c]=clo;
    					p=link(p);
    				}
    			}
    		}
    		return cur;
    	}
    	void topo_sort(){
    		static int in[maxn+5];
    		queue<int>q;
    		for(int i=1;i<=ptr;i++) in[link(i)]++;
    		for(int i=1;i<=ptr;i++) if(!in[i]) q.push(i);
    		while(!q.empty()){
    			int x=q.front();
    			q.pop();
    			in[link(x)]--;
    			t[link(x)].sta|=t[x].sta;
    			if(in[link(x)]==0) q.push(link(x));
    		}
    	}
    }T1;
    struct Trie{
    	int trie_pos[maxn+5],sam_pos[maxn+5];
    	int ch[maxn+5][maxc];
    	int sta[maxn+5];
    	const int root=1;
    	int ptr=1;
    	void insert(char *s,int id){
    		int top=0; 
    		int len=strlen(s+1);
    		trie_pos[++top]=root;
    		for(int i=1;i<=len;i++){
    			if(s[i]=='<') top--;
    			else{
    				int x=trie_pos[top],c=s[i]-'a';
    				if(!ch[x][c]) ch[x][c]=++ptr;
    				x=ch[x][c];
    				sta[x]|=(1<<(id-1)); 
    				trie_pos[++top]=x;
    			}
    		}
    	}
    	void build(EXSAM &T1){
    		queue<int>q;
    		q.push(root);
    		sam_pos[root]=root;
    		while(!q.empty()){
    			int x=q.front();
    			q.pop();
    			for(int i=0;i<maxc;i++){
    				int y=ch[x][i];
    				if(y!=0){
    					sam_pos[y]=T1.extend(sam_pos[x],i,sta[y]);
    					q.push(y);
    				}
    			}
    		}
    	}
    }T2; 
    int dp[maxs+5];
    int query(char *s){
    	int len=strlen(s+1);
    	int x=0;
    	for(int i=len;i>=1;i--) x=x*2+s[i]-'0';
    	return dp[x];
    }
    int main(){
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++){
    		scanf("%s",in+1);
    		T2.insert(in,i);
    	}
    	T2.build(T1);
    	T1.topo_sort();
    	for(int i=1;i<=T1.ptr;i++) dp[T1.t[i].sta]=max(dp[T1.t[i].sta],T1.t[i].len);
    	for(int i=(1<<n)-1;i>=0;i--){
    		for(int j=0;j<n;j++){
    			if(i&(1<<j)) dp[i^(1<<j)]=max(dp[i^(1<<j)],dp[i]);
    			//如果一个串的出现状态为i
    			//那么询问它的子集i^(1<<j)的答案,就可以用这个串去更新 
    		}
    	}
    	scanf("%d",&m);
    	for(int i=1;i<=m;i++){
    		scanf("%s",in+1);
    		printf("%d
    ",query(in));
    	}
    }
    /*
    2
    ab<a
    aac
    2
    01 
    */
    
    
  • 相关阅读:
    Log4Net使用
    4月博文
    论坛题目练习
    职场冷笑话两则
    初识管理的一些心得
    Project中分清楚挣值项
    预留规划项
    小感触
    好事多磨,好事成双
    忧郁
  • 原文地址:https://www.cnblogs.com/birchtree/p/12555638.html
Copyright © 2020-2023  润新知