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;
    }
    
  • 相关阅读:
    Candy leetcode java
    Trapping Rain Water leetcode java
    Best Time to Buy and Sell Stock III leetcode java
    Best Time to Buy and Sell Stock II leetcode java
    Best Time to Buy and Sell Stock leetcode java
    Maximum Subarray leetcode java
    Word Break II leetcode java
    Word Break leetcode java
    Anagrams leetcode java
    Clone Graph leetcode java(DFS and BFS 基础)
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ4044.html
Copyright © 2011-2022 走看看