感觉这道题目有点意思,希望他不会被我一眼秒了。
好像有一个有点正确(?)的嘴巴,找出最大的偶回文串,然后这个是可以通过操作二减少步骤的。
然后考虑最大的偶回文串一定不能向外执行操作二了?好像是的。
这样我们就转换成了子问题,如何快速求出最大偶回文串的一半的长度。
发现每一次长度必然减半,所以最多只会执行 (log_2n) 次,所以直接考虑每次用 (PAM) 或马拉车求一下就行了。
等等我发现一个巨大问题,这个子问题转换一定最优吗?
如果碰到了相同大小的回文子串该怎么办呢?
可能当前决策是最优的但是不满足全局最优啊。。。
咦等下,我们考虑本质不同的回文子串的个数是只有 (O(n)) 个的,所以说我们能否把所有的子串的答案求出来?
哦,我们考虑在回文树上一个子串如果是另一个子串的回文后缀,那么其一半也必然满足后缀关系。
我们可以考虑从回文树的顶端向下遍历,考虑往前加字符,然后求出该一半的对应值,这个应该是在后缀树上 (dp) ,我们需要类似树剖倍增和线段树的东西来搞就行了。然后再多考虑一个转移即可。
发现还是过不了样例,自闭了,但是感觉方法没错,我不想调了。
发现你根本不用考虑奇串,考虑回文串的性质,更优的偶串会在前面就更新。
代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int n;char s[N];
int trans(char c){
if(c=='A') return 0;
if(c=='T') return 1;
if(c=='G') return 2;
return 3;
}
char trans(int x){
if(x==0) return 'A';
if(x==1) return 'T';
if(x==2) return 'G';
return 'C';
}
struct PAM{
int rt0,rt1,len,lst,tot;
struct Node{int fail,len,go[4];}tr[N];
PAM(){rt0=1,init();}
void init(){
for(int i=0;i<=tot;++i) tr[i]=Node();
lst=len=0,tot=1,tr[rt1].len=-1,tr[rt0].fail=rt1;
}
int find(int u,char c){
while(s[len-tr[u].len-1]!=c) u=tr[u].fail;
return u;
}
void insert(char c){
int u=lst;len++;u=find(u,c);
if(!tr[u].go[trans(c)]){
tr[u].go[trans(c)]=++tot,tr[tot].len=tr[u].len+2;
if(tr[tot].len==1) tr[tot].fail=rt0;
else tr[tot].fail=tr[find(tr[u].fail,c)].go[trans(c)];
}
lst=tr[u].go[trans(c)];
}
}pam;
struct Tree{
int n;
struct Edge{int nxt,to;}e[N];int fir[N];
void add(int u,int v,int i){e[i]=(Edge){fir[u],v},fir[u]=i;}
struct Node{int len,f,fa,deg,fail[20],go[4];}tr[N];
void init(){
for(int i=1;i<=n;++i) fir[i]=0,tr[i]=Node();
}
void fuck(){
for(int j=1;j<20;++j){
for(int i=1;i<=n;++i)
tr[i].fail[j]=tr[tr[i].fail[j-1]].fail[j-1];
}
}
void dp(){
queue<int> q;
for(int i=1;i<=n;++i) if(!tr[i].deg) q.push(i);
while(!q.empty()){
int u=q.front();q.pop();
if(tr[u].len&1) tr[u].f=tr[u].len;
else{
int v=u;
for(int i=19;i>=0;--i){
if(tr[tr[v].fail[i]].len>tr[u].len/2)
v=tr[v].fail[i];
}
v=tr[v].fail[0];
tr[u].f=min(tr[v].f+tr[u].len/2-tr[v].len+1,tr[tr[u].fa].f+1);
}
for(int i=fir[u];i;i=e[i].nxt){
int v=e[i].to;tr[v].deg--;
if(!tr[v].deg) q.push(v);
}
for(int i=0;i<4;++i){
if(!tr[u].go[i]) continue;
tr[tr[u].go[i]].deg--;
if(!tr[tr[u].go[i]].deg)
q.push(tr[u].go[i]);
}
}
}
}t;
int solve(){
scanf("%s",s+1),n=strlen(s+1);
pam.init(),t.init();
for(int i=1;i<=n;++i) pam.insert(s[i]);
t.n=pam.tot;
for(int i=1;i<=t.n;++i){
t.tr[i].deg++;
t.tr[i].len=pam.tr[i].len;
t.tr[i].fail[0]=pam.tr[i].fail;
t.add(t.tr[i].fail[0],i,i);
for(int j=0;j<4;++j)
t.tr[i].go[j]=pam.tr[i].go[j];
for(int j=0;j<4;++j){
if(!pam.tr[i].go[j]) continue;
t.tr[pam.tr[i].go[j]].fa=i;
t.tr[pam.tr[i].go[j]].deg++;
}
}
t.tr[1].deg--,t.fuck(),t.dp();
int res=1e9+7;
for(int i=1;i<=t.n;++i)
res=min(res,t.tr[i].f+n-t.tr[i].len);
return printf("%d
",res),0;
}
int main(){
int T;cin>>T;
while(T--) solve();
return 0;
}