P4762 [CERC2014]Virus synthesis

时间:2021-08-10
本文章向大家介绍P4762 [CERC2014]Virus synthesis,主要包括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\n",res),0;
}
int main(){
	int T;cin>>T;
	while(T--) solve();
	return 0;
}

原文地址:https://www.cnblogs.com/Point-King/p/15123898.html