题意
真是道回文自动机好题。
首先考虑答案必定是一个回文串+剩余部分的形式,因此可以建出回文自动机,之后考虑每个长度为偶数的回文串。
对于一个长度为偶数的回文串,设它在回文自动机上对应的节点为(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;
}