• P4762 [CERC2014]Virus synthesis


    感觉这道题目有点意思,希望他不会被我一眼秒了。

    好像有一个有点正确(?)的嘴巴,找出最大的偶回文串,然后这个是可以通过操作二减少步骤的。

    然后考虑最大的偶回文串一定不能向外执行操作二了?好像是的。

    这样我们就转换成了子问题,如何快速求出最大偶回文串的一半的长度。

    发现每一次长度必然减半,所以最多只会执行 (log_2n) 次,所以直接考虑每次用 (PAM) 或马拉车求一下就行了。

    等等我发现一个巨大问题,这个子问题转换一定最优吗?

    如果碰到了相同大小的回文子串该怎么办呢?

    可能当前决策是最优的但是不满足全局最优啊。。。

    咦等下,我们考虑本质不同的回文子串的个数是只有 (O(n))​ 个的,所以说我们能否把所有的子串的答案求出来?

    哦,我们考虑在回文树上一个子串如果是另一个子串的回文后缀,那么其一半也必然满足后缀关系。

    我们可以考虑从回文树的顶端向下遍历,考虑往前加字符,然后求出该一半的对应值,这个应该是在后缀树上 (dp) ,我们需要类似树剖倍增和线段树的东西来搞就行了。然后再多考虑一个转移即可。

    发现还是过不了样例,自闭了,但是感觉方法没错,我不想调了。


    发现你根本不用考虑奇串,考虑回文串的性质,更优的偶串会在前面就更新。

    代码

    #include<bits/stdc++.h>
    using namespace std;
    const int N=1e5+5;
    int n;char s[N];
    int trans(char c){
    	if(c=='A') return 0;
    	if(c=='T') return 1;
    	if(c=='G') return 2;
    	return 3;
    }
    char trans(int x){
    	if(x==0) return 'A';
    	if(x==1) return 'T';
    	if(x==2) return 'G';
    	return 'C';
    }
    struct PAM{
    	int rt0,rt1,len,lst,tot;
    	struct Node{int fail,len,go[4];}tr[N];
    	PAM(){rt0=1,init();}
    	void init(){
    		for(int i=0;i<=tot;++i) tr[i]=Node();
    		lst=len=0,tot=1,tr[rt1].len=-1,tr[rt0].fail=rt1;
    	}
    	int find(int u,char c){
    		while(s[len-tr[u].len-1]!=c) u=tr[u].fail;
    		return u;
    	}
    	void insert(char c){
    		int u=lst;len++;u=find(u,c);
    		if(!tr[u].go[trans(c)]){
    			tr[u].go[trans(c)]=++tot,tr[tot].len=tr[u].len+2;
    			if(tr[tot].len==1) tr[tot].fail=rt0;
    			else tr[tot].fail=tr[find(tr[u].fail,c)].go[trans(c)];
    		}
    		lst=tr[u].go[trans(c)];
    	}
    }pam;
    struct Tree{
    	int n;
    	struct Edge{int nxt,to;}e[N];int fir[N];
    	void add(int u,int v,int i){e[i]=(Edge){fir[u],v},fir[u]=i;}
    	struct Node{int len,f,fa,deg,fail[20],go[4];}tr[N];
    	void init(){
    		for(int i=1;i<=n;++i) fir[i]=0,tr[i]=Node();
    	}
    	void fuck(){
    		for(int j=1;j<20;++j){
    			for(int i=1;i<=n;++i)
    			tr[i].fail[j]=tr[tr[i].fail[j-1]].fail[j-1];
    		}
    	}
    	void dp(){
    		queue<int> q;
    		for(int i=1;i<=n;++i) if(!tr[i].deg) q.push(i);
    		while(!q.empty()){
    			int u=q.front();q.pop();
    			if(tr[u].len&1) tr[u].f=tr[u].len;
    			else{
    				int v=u;
    				for(int i=19;i>=0;--i){
    					if(tr[tr[v].fail[i]].len>tr[u].len/2)
    					v=tr[v].fail[i];
    				}
    				v=tr[v].fail[0];
    				tr[u].f=min(tr[v].f+tr[u].len/2-tr[v].len+1,tr[tr[u].fa].f+1);
    			}
    			for(int i=fir[u];i;i=e[i].nxt){
    				int v=e[i].to;tr[v].deg--;
    				if(!tr[v].deg) q.push(v);
    			}
    			for(int i=0;i<4;++i){
    				if(!tr[u].go[i]) continue;
    				tr[tr[u].go[i]].deg--;
    				if(!tr[tr[u].go[i]].deg)
    					q.push(tr[u].go[i]);
    			}
    		}
    	}
    }t;
    int solve(){
    	scanf("%s",s+1),n=strlen(s+1);
    	pam.init(),t.init();
    	for(int i=1;i<=n;++i) pam.insert(s[i]);
    	t.n=pam.tot;
    	for(int i=1;i<=t.n;++i){
    		t.tr[i].deg++;
    		t.tr[i].len=pam.tr[i].len;
    		t.tr[i].fail[0]=pam.tr[i].fail;
    		t.add(t.tr[i].fail[0],i,i);
    		for(int j=0;j<4;++j)
    			t.tr[i].go[j]=pam.tr[i].go[j];
    		for(int j=0;j<4;++j){
    			if(!pam.tr[i].go[j]) continue;
    			t.tr[pam.tr[i].go[j]].fa=i;
    			t.tr[pam.tr[i].go[j]].deg++;
    		}
    	}
    	t.tr[1].deg--,t.fuck(),t.dp();
    	int res=1e9+7;
    	for(int i=1;i<=t.n;++i)
    		res=min(res,t.tr[i].f+n-t.tr[i].len);
    	return printf("%d
    ",res),0;
    }
    int main(){
    	int T;cin>>T;
    	while(T--) solve();
    	return 0;
    }
    
  • 相关阅读:
    操作系统简介
    计算机基础
    Django之form
    CMDB资产采集
    Git
    User model
    多级评论
    个人主页
    media路径设置
    Web框架
  • 原文地址:https://www.cnblogs.com/Point-King/p/15123898.html
Copyright © 2020-2023  润新知