题目链接:4044:[Cerc2014] Virus synthesis
真是一道回文自动机的好题
先建立一发回文自动机,然后设dp[i]为形成节点i所代表的回文串所需最少步骤数,l[i]是i节点代表的回文串的长度
dp[i]的初值要设为l[i],表示最多l[i]步就可以形成i这个回文串
对于串i,他可以在首或者尾加一个字符形成的回文串j:dp[j]=dp[i]+1;
然后我们可以根据j的fail指针找到一个回文串x,这个回文串的长度小于等于l[j]/2,则dp[j]=min(dp[j],dp[x]+1+l[j]/2-l[x]);
我们可以用一个队列存储可以转移的状态,这样就保证了转移是有序的
但是还不够,对于像全是A这样的数据会T
因为我们对于每一个串都会根据fail指针跳很多次
我们考虑预先处理出每个j对应的x,设为t[];
我们可以在构造完fail指针后继续构造t[];
但是不行还会T,即使是这样我们调用比较函数的次数还会高达3亿次!
考虑我们在构造t[j]的时候t[last]是已经构造出来的了,由于j是在last的基础上又跳了一次fail指针,所以t[j]的长度一定不会大于t[last]的长度且我们以前的方法再跳fail指针的时候一定会调到过last,所以我们可以直接以t[last]为基础开始跳
不要以为这个优化不明显……这样只用调用比较函数不到10万次QAQ
然后就过了QAQ
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=200010;
const int maxs=5;
int n,ans,dp[maxn],q[maxn];
char s[maxn];
inline int getw(char c){
if (c=='A') return 0;
else if (c=='T') return 1;
else if (c=='C') return 2;
else return 3;
}
struct pam{
int next[maxn][maxs],ro,re,t[maxn],N;
int last,sz,l[maxn],fail[maxn],str[maxn];
void init(){
sz=0; str[0]=-1; N=0;
++sz; ro=sz; l[ro]=-1; memset(next[sz],0,sizeof(next[sz]));
++sz; re=sz; l[re]=0; memset(next[sz],0,sizeof(next[sz]));
fail[re]=ro; last=ro;
}
bool gofail(int x,int c,int id){
return str[N-l[x]-1]==c;
}
void add(int c,int id){
str[++N]=c;
while (!gofail(last,c,id)) last=fail[last];
if (next[last][c]) last=next[last][c];
else{
int x=last; ++sz; next[x][c]=sz; l[sz]=l[x]+2;
memset(next[sz],0,sizeof(next[sz])); int z=last;
if (x==ro) fail[sz]=re;
else{
x=fail[x];
while (!gofail(x,c,id)) x=fail[x];
fail[sz]=next[x][c];
}last=sz;
if (l[sz]<=2) t[sz]=fail[sz];
else {
z=t[z];
while (!gofail(z,c,id)||(l[z]+2)*2>l[sz]) z=fail[z];
t[sz]=next[z][c];
}
}
}
}pt;
int main(){
int T; scanf("%d",&T);
for (int balabala=1;balabala<=T;++balabala){
scanf("%s",s+1);
int n=strlen(s+1);
pt.init(); ans=n;
for (int i=1;i<=n;++i) pt.add(getw(s[i]),i);
for (int i=3;i<=pt.sz;++i) dp[i]=pt.l[i];
int h=1,tail=0,now; q[++tail]=2; dp[2]=1;
while (h<=tail){
now=q[h++];
for (int i=0;i<4;++i){
int x=pt.next[now][i];
if (!x) continue;
dp[x]=dp[now]+1;
int y=pt.t[x];
dp[x]=min(dp[x],dp[y]+1+pt.l[x]/2-pt.l[y]);
ans=min(ans,dp[x]+n-pt.l[x]);
q[++tail]=x;
}
}printf("%d\n",ans);
}
}