考虑最后一次翻倍操作,操作后就是一个回文串,然后不断向两边添加字符得到原串
我们建出回文自动机,考虑对每个回文串dp出建出他需要的最少步数
若g[i]表示构建出回文串i的最少步数
为了方便,我设的dp状态是f[i]=g[i]-i
对于一个奇数回文串A,令B为A去掉尾部字符的串,C为A去掉两端字符得到的回文串,
若B是回文串,D=B,否则D=C
那么考虑我们构建串A最后一次翻倍操作后的添加操作,如果全部在头部添加
f
[
A
]
=
f
[
f
a
i
l
[
A
]
]
f[A]=f[fail[A]]
f[A]=f[fail[A]]
如果有在尾部添加的操作
f
[
A
]
=
f
[
D
]
f[A]=f[D]
f[A]=f[D]
对于每个奇数回文串A,D可以在构建回文自动机的时候顺便处理出来
对于一个偶数回文串E,令F为E去掉两端字符的回文串,G为p从E开始,不断跳fail[p]直到len[p]<=len[E]
可以用反证法证明E最后一次操作一定是翻倍,考虑倒数第二次翻倍操作之后的操作,如果全部是在头部添加
f
[
E
]
=
f
[
G
]
−
l
e
n
[
E
]
/
2
+
1
f[E]=f[G]-len[E]/2+1
f[E]=f[G]−len[E]/2+1
如果有在尾部添加的
f
[
E
]
=
f
[
F
]
−
1
f[E]=f[F]-1
f[E]=f[F]−1
注意判F,G不存在的情况
这个F可以也在构建回文自动机的时候处理出来,G的话要构建完自动机后把fail树建出来,dfs一遍fail树求出每个点的G
code:
#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
const int maxn = 210000;
const int maxc = 4;
int n;
char str[maxn]; int S[maxn];
struct Tree
{
int son[maxn][maxc],fail[maxn],len[maxn],tot,last;
int pre[maxn];
void init()
{
tot=1; last=0;
memset(son[0],0,sizeof son[0]);
memset(son[1],0,sizeof son[1]);
fail[0]=1,fail[1]=0;
len[0]=0,len[1]=-1;
}
int newnode(int l)
{
++tot;
memset(son[tot],0,sizeof son[tot]);
len[tot]=l,fail[tot]=0;
pre[tot]=0;
return tot;
}
void extend(const int i)
{
int p=last,w=S[i],lp=0,np;
while(S[i-1-len[p]]!=w) lp=p,p=fail[p];
if(!son[p][w])
{
np=newnode(len[p]+2);
if(len[np]&1) pre[np]=len[lp]+1==len[np]?lp:p;
else pre[np]=p;
int t=fail[p];
while(S[i-1-len[t]]!=w) t=fail[t];
fail[np]=son[t][w];
son[p][w]=np;
}
else np=son[p][w];
last=np;
}
}Tr;
struct edge{int y,nex;}a[maxn]; int len,fir[maxn];
inline void ins(const int x,const int y){a[++len]=(edge){y,fir[x]};fir[x]=len;}
int mark[maxn],nex[maxn];
void dfs(const int x,int la)
{
if(x>1)
{
while(Tr.len[nex[la]]*2<=Tr.len[x]) la=nex[la];
if(Tr.len[x]%2==0) mark[x]=la;
}
for(int k=fir[x],y=a[k].y;k;k=a[k].nex,y=a[k].y)
nex[x]=y,dfs(y,la);
}
int f[maxn];
int main()
{
//freopen("tmp.in","r",stdin);
//freopen("tmp.out","w",stdout);
int T;scanf("%d",&T);
while(T--)
{
Tr.init();
scanf("%s",str); n=strlen(str);
S[0]=-1;
for(int i=0;i<n;i++)
{
if(str[i]=='A') S[i+1]=0;
else if(str[i]=='G') S[i+1]=1;
else if(str[i]=='C') S[i+1]=2;
else S[i+1]=3;
}
for(int i=1;i<=n;i++)
Tr.extend(i);
len=0; for(int i=0;i<=Tr.tot;i++) fir[i]=mark[i]=0;
for(int i=0;i<=Tr.tot;i++) if(i!=1) ins(Tr.fail[i],i);
dfs(1,1);
int ans=0;
for(int i=2;i<=Tr.tot;i++)
{
if(Tr.len[i]&1) f[i]=min(f[Tr.fail[i]],f[Tr.pre[i]]);
else
f[i]=min(f[Tr.pre[i]]-(Tr.len[Tr.pre[i]]>0),!mark[i]?0:(f[mark[i]]-Tr.len[i]/2+1));
ans=min(ans,f[i]);
}
printf("%d\n",n+ans);
}
return 0;
}