zoukankan      html  css  js  c++  java
  • 【BZOJ4044】[CERC2014] Virus synthesis(回文自动机上DP)

    点此看题面

    大致题意: 有一个字符串(S),初始为空,你可以进行两种操作:在(S)的前面或后面添加一个字符;将(S)复制并翻转得到(S'),然后把(S')接到(S)后面。求最少需要几次操作才能得到给定串。

    回文自动机

    关于回文自动机可以看这篇博客:初学回文自动机

    这里补充一个相关定义:(trans[x])。表示(x)所代表的回文串中,长度小于等于(x)长度一半的最长回文后缀所对应的节点。

    至于如何求出(trans[x]),不难发现它和(fail[x])定义很像,因此求法也差不多,只要在(while)循环中加上一句判断长度是否大于(x)长度一半即可。

    具体实现可详见代码。

    动态规划

    (f_x)表示得到节点(x)表示的字符串所需的最小代价。

    由于回文自动机上的字符串都是回文串,因此我们(BFS)遍历回文自动机,然后每次枚举(k)的后继状态(x),显然有两种转移:

    • 直接从(k)在首位各添一个字符得到,由于有回文操作,因此这一步实际上只需要(1)的代价。即:

    [f_x=f_k+1 ]

    • (y=trans[x])前面添上若干字符,然后回文操作得到(x)(注意这里只需考虑在前面添上字符,是因为在后面添上字符会在上面的转移中考虑到)。即:

    [f_x=f_{y}+1+(frac{len[x]}2-len[y]) ]

    注意由于奇回文串无法通过回文操作得到,在此题中没有贡献,因此(BFS)开始时我们只需将偶根加入队列并令(f_0=1)即可。

    最终答案就是(min{f_x+(n-len[x])})

    代码

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 100000
    using namespace std;
    int n;char s[N+5];
    class FastIO
    {
    	private:
    		#define FS 100000
    		#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
    		#define pc(c) (C==E&&(clear(),0),*C++=c)
    		#define D isdigit(c=tc())
    		int T;char c,*A,*B,*C,*E,FI[FS],FO[FS],S[FS];
    	public:
    		I FastIO() {A=B=FI,C=FO,E=FO+FS;}
    		Tp I void read(Ty& x) {x=0;W(!D);W(x=(x<<3)+(x<<1)+(c&15),D);}
    		Tp I void writeln(Ty x) {W(S[++T]=x%10+48,x/=10);W(T) pc(S[T--]);pc('
    ');}
    		I void clear() {fwrite(FO,1,C-FO,stdout),C=FO;}
    }F;
    class PalindromeAutomation//回文自动机
    {
    	private:
    		int Nt,lst,q[N+5];struct node {int DP,L,T,F,S[4];}O[N+5];
    		I int Fail(RI x,CI id) {W(s[id-O[x].L-1]^s[id]) x=O[x].F;return x;}//跳Fail
    	public:
    		I void Init() {memset(O,0,sizeof(node)*Nt),O[O[lst=0].F=Nt=1].L=-1;}//初始化清空
    		I void Ins(CI id)//插入新元素
    		{
    			#define GV(c) (c=='A'?0:(c=='T'?1:(c=='G'?2:3)))
    			RI x=GV(s[id]),t=Fail(lst,id),o;if(!O[t].S[x])//如果没有该儿子
    			{
    				O[o=++Nt].L=O[t].L+2,O[o].F=O[Fail(O[t].F,id)].S[x],O[t].S[x]=o;//计算信息
    				if(O[o].L<=2) O[o].T=O[o].F;else//求trans
    				{
    					RI k=O[t].T;W(s[id-O[k].L-1]^s[id]||(O[k].L+2<<1)>O[o].L) k=O[k].F;//注意判断长度是否大于一半
    					O[o].T=O[k].S[x];//记录trans
    				}
    			}lst=O[t].S[x];//更新lst
    		}
    		I int Work()//DP
    		{
    			for(RI i=1;i<=Nt;++i) O[i].DP=1e9;//初始化
    			RI i,k,x,y,ans=n,H=1,T=0;O[q[++T]=0].DP=1;W(H<=T)//BFS
    			{
    				for(k=q[H++],i=0;i^4;++i) (x=O[k].S[i])&&//枚举后继
    				(
    					y=O[x].T,O[x].DP=min(O[k].DP+1,O[y].DP+1+(O[x].L>>1)-O[y].L),//DP转移
    					ans=min(ans,O[x].DP+n-O[x].L),q[++T]=x//统计答案,加入BFS队列
    				);
    			}return ans;
    		}
    }P;
    int main()
    {
    	RI Tt,i;scanf("%d",&Tt);W(Tt--)
    	{
    		for(scanf("%s",s+1),n=strlen(s+1),P.Init(),i=1;i<=n;++i) P.Ins(i);//建回文自动机
    		printf("%d
    ",P.Work());//输出答案
    	}return 0;
    }
    
  • 相关阅读:
    html总结:背景图片拉伸
    html总结:表格中的文字居中
    html总结:float实现span和input输入框同行
    servlet总结:Servlet基础
    js总结:JavaScript入门
    河北科技创新平台年报系统涉众分析
    确定业务问题的范围-上下文的范围模型
    问题账户需求分析
    Struts2------OGNL表达式
    Struts入门
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ4044.html
Copyright © 2011-2022 走看看