传送门
题解:
很显然按照这个构造方式,我们一定是在构造出某一个偶数长度的回文串之后把剩下补齐。
可以很显然地发现奇数回文串没有作为最终串的价值,但是我们可以构造奇数串,复制成偶数串。
设 m n [ u ] mn[u] mn[u]表示构造出串 u u u需要的最小次数,对于奇数串我们有 m n [ u ] = l e n [ u ] mn[u]=len[u] mn[u]=len[u],对于偶数串,我们考虑两种转移:
- 从PAM的转移数组转移,显然如果 s o n [ u ] [ i ] = v son[u][i]=v son[u][i]=v,则我们考虑在构造出 u u u的一半的时候加上一个字符 i i i,然后复制成 v v v,则 m n [ v ] = m n [ u ] + 1 mn[v]=mn[u]+1 mn[v]=mn[u]+1,为此我们需要初始化 m n [ 0 ] = 1 mn[0]=1 mn[0]=1。
- 从fail树上转移,我们找到它的最长的不超过它一半长度的回文后缀,则我们考虑将这个后缀补齐到一半长度,然后复制一次,记这个回文后缀为 h a l f [ v ] half[v] half[v],则 m n [ v ] = l e n [ v ] / 2 − l e n [ h a l f [ v ] ] + m n [ h a l f [ v ] ] + 1 mn[v]=len[v]/2-len[half[v]]+mn[half[v]]+1 mn[v]=len[v]/2−len[half[v]]+mn[half[v]]+1
两者取 m i n min min就行了,最终答案为 min ( n − l e n [ u ] + m n [ u ] ) \min(n-len[u]+mn[u]) min(n−len[u]+mn[u])。
代码:
#include<bits/stdc++.h>
#define ll long long
#define re register
#define gc get_char
#define cs const
namespace IO{
inline char get_char(){
static cs int Rlen=1<<22|1;
static char buf[Rlen],*p1,*p2;
return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
}
inline int get_s(char *s){
int len=0;char c;
while(isspace(c=gc()));
while(s[len++]=c,!isspace(c=gc())&&c!=EOF);
return s[len]='\0',len;
}
}
using namespace IO;
using std::cerr;
using std::cout;
cs int N=1e5+5;
inline int id(char c){
switch(c){
case 'A':return 1;
case 'T':return 2;
case 'C':return 3;
case 'G':return 0;
default:assert(0);
}
}
char s[N];int n;
namespace PAM{
int son[N][4],fa[N],len[N],now,last;
int mn[N],half[N];
inline void init(){
while(~now){
memset(son[now],0,sizeof son[now]);
fa[now]=len[now]=mn[now]=half[now]=0;
--now;
}
last=0,now=1;
fa[0]=fa[1]=1,len[1]=-1;
}
inline void push_back(char c,int i){c=id(c);
int p=last;
while(s[i]!=s[i-len[p]-1])p=fa[p];
if(!son[p][c]){
len[++now]=len[p]+2;
int k=fa[p];
while(s[i]!=s[i-len[k]-1])k=fa[k];
fa[now]=son[k][c],son[p][c]=now;
k=half[p];
while(s[i]!=s[i-len[k]-1])k=fa[k];
k=son[k][c];
while(len[k]>(len[now]>>1))k=fa[k];
half[now]=k;
}
last=son[p][c];
}
inline void solve(){
int ans=n=get_s(s+1);
for(int re i=1;i<=n;++i)push_back(s[i],i);
for(int re i=2;i<=now;++i)if(len[i]&1)mn[i]=len[i];
mn[0]=1;std::queue<int> q;q.push(0);
while(!q.empty()){
int u=q.front();q.pop();
for(int re i=0,v;i<4;++i)if(v=son[u][i]){
mn[v]=std::min(mn[u]+1,len[v]/2-len[half[v]]+mn[half[v]]+1);
ans=std::min(ans,n-len[v]+mn[v]);
q.push(v);
}
}
cout<<ans<<"\n";
}
}
signed main(){
// freopen("virus.in","r",stdin);freopen("virus.out","w",stdout);
int T;scanf("%d",&T);
while(T--)PAM::init(),PAM::solve();
return 0;
}