【BZOJ】4044: [Cerc2014] Virus synthesis-回文自动机

传送门:bzoj4044


题解

r e v e r s e reverse reverse操作可以生成一个长度为偶的回文串。

对于长度 ≥ 4 \geq 4 4的回文串,用 r e v e r s e reverse reverse来构造相比直接加会使得操作次数减少, = 2 =2 =2时也不亏。那么问题就转化成了找到一个极大的偶回文串来优化答案。

在这道题,完全不需要考虑到奇回文串,因为若其内部不包含偶回文,就不能执行 r e v e r s e reverse reverse,若其内部包含偶回文,也只需要考虑这个内部的偶回文再在外部插入单个字符即可。

设偶回文 i i i的长度为 l e n i len_i leni,最少构造操作次数为 f i f_i fi
设字符串总长为 n n n,则 a n s = m i n ( f i + n − l e n i ) ans=min(f_i+n-len_i) ans=min(fi+nleni)

构造回文自动机,递推得到 f i f_i fi

设偶回文串 i i i首尾分别去掉一个字符得到 f a t i fat_i fati,则 f i = m i n ( f i , f f a t i + 1 ) f_i=min(f_i,f_{fat_i}+1) fi=min(fi,ffati+1)(末尾填一个字符后 r e v e r s e reverse reverse)
设长度 ≤ l e n i 2 \leq\dfrac{len_i}{2} 2leni的最长的 S i S_i Si的后缀偶回文串为 l a s i las_i lasi,则 f i = m i n ( f i , f l a s i + l e n i 2 − l e n l a s i + 1 ) f_i=min(f_i,f_{las_i}+\dfrac{len_i}{2}-len_{las_i}+1) fi=min(fi,flasi+2lenilenlasi+1)

注意不能每次暴力跳父边找 l a s i las_i lasi,需要记录 l a s f a t i las_{fat_i} lasfati后向下找,才能保证复杂度是线性的(奇偶回文串都要记录 l a s las las)


代码

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+100;

int tk,n,p,ans,w[N],las[N];
int len[N],f[N],ch[N][26],cnt;
char s[N];

inline int new_node(int x)
{len[cnt]=x;ch[cnt][0]=ch[cnt][2]=ch[cnt][6]=ch[cnt][19]=0;f[cnt]=0;return cnt++;}
inline int get_fail(int p,int m)
{for(;s[m-len[p]]!=s[m+1];p=f[p]);return p;}

inline void ins(int alp,int m)
{
	p=get_fail(p,m);
	if(!ch[p][alp]){
		int i,cur=new_node(len[p]+2);
		if(!(len[p]&1)) w[cur]=min(len[cur],w[p]+1);
		f[cur]=ch[get_fail(f[p],m)][alp];
		ch[p][alp]=cur;
		if(len[cur]<=2) las[cur]=f[cur];
		else{
			for(i=las[p];s[m-len[i]]!=s[m+1] || (((len[i]+2)<<1)>len[cur]);i=f[i]);
			las[cur]=ch[i][alp];if(!(len[cur]&1)){
				for(i=las[cur];i>1 && (len[i]&1);i=f[i]);
				if(i>1) w[cur]=min(w[cur],w[i]+len[cur]/2-len[i]+1);
			}
		}
	}
	p=ch[p][alp];
}

inline void sol()
{
	int i,j,k;
    scanf("%s",s+1);w[0]=1;n=strlen(s+1);cnt=0;
	f[new_node(0)]=new_node(-1);
    p=0;ans=n;for(i=1;i<=n;++i) ins(s[i]-'A',i-1);
    for(i=2;i<cnt;++i) if(!(len[i]&1)) ans=min(ans,w[i]+n-len[i]);
    printf("%d\n",ans);
}

int main(){
	for(scanf("%d",&tk);tk;--tk) sol();
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值