P4762 [CERC2014]Virus synthesis

题意

真是道回文自动机好题。

首先考虑答案必定是一个回文串+剩余部分的形式,因此可以建出回文自动机,之后考虑每个长度为偶数的回文串。

对于一个长度为偶数的回文串,设它在回文自动机上对应的节点为(x),我们对于每个(x)求出(trans_x)表示x的最长后缀回文串,满足(len_{trans_x}leqslant len_x/2)

之后设(f_x)表示(x)拼成(x)这个串的最小代价,我们从(0)(偶根)出发进行(bfs),中途计算(f_x)

对于(f_x)
初值肯定是自身长度(f_x=len_x)
如果存在一条边((x,y)),那么(f_y=f_x+1),因为我们可以在拼(x)时还没进行(2)操作时向(x)后面填一个字符,使其进行(2)操作后变为(y)
同时(f_x=min(f_x,f_{trans_x}+1+lem_x/2-len_{trans_x})),即我们可以从(trans_x)变过来。

对于每个(x),它对答案的贡献是(n-len_x+f_x)(n)是字符串长度。

code:

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
const int inf=1e9;
int T,n,tot,last,ans;
int fail[maxn],len[maxn],trans[maxn],f[maxn];
int ch[maxn][5];
char s[maxn];
inline int change(char c)
{
	if(c=='A')return 1;
	if(c=='G')return 2;
	if(c=='C')return 3;
	if(c=='T')return 4;
	return 2333;
}
inline void init()
{
	for(int i=0;i<=tot;i++)
		for(int j=1;j<=4;j++)
			ch[i][j]=0;
	fail[0]=1;len[1]=-1;
	tot=1;last=0;
}
inline int getfail(int x,int pos)
{
	while(s[pos-len[x]-1]!=s[pos])x=fail[x];
	return x;
}
inline void add(int c,int pos)
{
	int p=getfail(last,pos);
	if(!ch[p][c])
	{
		int q=++tot,tmp;len[q]=len[p]+2;
		tmp=getfail(fail[p],pos);
		fail[q]=ch[tmp][c];ch[p][c]=q;
		if(len[q]<=2)trans[q]=fail[q];
		else  
		{
			tmp=trans[p];
			while(s[pos-len[tmp]-1]!=s[pos]||((len[tmp]+2)<<1)>len[q])tmp=fail[tmp];
			trans[q]=ch[tmp][c];
		}
	}
	last=ch[p][c];
}
inline void solve()
{
	queue<int>q;
	for(int i=2;i<=tot;i++)f[i]=len[i];
	for(int i=1;i<=4;i++)if(ch[0][i])q.push(ch[0][i]);
	while(!q.empty())
	{
		int x=q.front();q.pop();
		f[x]=min(f[x],f[trans[x]]+1+len[x]/2-len[trans[x]]);
		ans=min(ans,n-len[x]+f[x]);
		for(int i=1;i<=4;i++)
		{
			if(!ch[x][i])continue;
			int y=ch[x][i];
			f[y]=min(f[y],f[x]+1);
			q.push(y);
		}
	}
}
int main()
{
	scanf("%d",&T);
	while(T--)
	{
		scanf("%s",s+1);n=strlen(s+1);
		s[0]='#';
		init();
		for(int i=1;i<=n;i++)add(change(s[i]),i);
		ans=n;solve();
		printf("%d
",ans);
	}
	return 0;	
}
原文地址:https://www.cnblogs.com/nofind/p/12070004.html