把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【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('\n');}
		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\n",P.Work());//输出答案
	}return 0;
}
posted @ 2020-07-20 21:29  TheLostWeak  阅读(135)  评论(0编辑  收藏  举报