jzoj4387 [GDOI2016模拟3.15]基因合成 回文树+dp
程序员文章站
2024-02-12 23:16:22
...
Description
Solution
60分看完就会了,用头dp
打完然后开始猜结论。最小步数肯定是先变成偶数回文串再往两边加,然后想到了一个manacher乱搞做法,就不会了
对付回文子串可以考虑用回文树。我们知道回文树上每个节点都代表一个字符串,且他的fail指向他的一个最长后缀回文字串,那么我们可以在树上dp。用f[i]表示节点i所代表的串要多少步拼接,转移讨论:
1. 两边加一个。这个可以变成转移之前的节点添加一个字符
2. 由一个长度小于一半的串添加翻转
Code
#include <stdio.h>
#include <string.h>
#include <algorithm>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define fill(x,t) memset(x,t,sizeof(x))
const int INF=0x3f3f3f3f;
const int N=200005;
char str[N];
int f[N];
struct PalindromeAutomaton {
int fail[N],rec[N][26],len[N],tot,last;
void clear() {
fill(rec,0); fill(len,0); fill(fail,0); fill(f,31);
tot=last=2; fail[1]=fail[2]=1; len[1]=-1; len[2]=0; f[2]=1; f[1]=2;
}
void ins(int ch,int n) {
int p=last;
while (str[n]!=str[n-1-len[p]]) p=fail[p];
if (!rec[p][ch]) {
rec[p][ch]=++tot; len[tot]=len[p]+2;
last=rec[p][ch];
if (len[last]==1) {
fail[last]=2; f[last]=1;
return ;
}
int k=fail[p];
while (str[n]!=str[n-len[k]-1]) k=fail[k];
fail[tot]=rec[k][ch];
if (len[last]&1) f[last]=len[last];
else {
k=fail[last];
while (len[k]>len[last]/2) k=fail[k];
f[last]=std:: min(f[p]+1,f[k]+(len[last]/2-len[k])+1);
}
} else last=rec[p][ch];
}
} pam;
int main(void) {
int T; scanf("%d",&T);
for (;T--;) {
pam.clear();
scanf("%s",str+1);
int len=strlen(str+1),ans=INF;
rep(i,1,len) {
pam.ins(str[i]-'A',i);
}
rep(i,3,pam.tot) ans=std:: min(ans,f[i]+len-pam.len[i]);
printf("%d\n", ans);
}
return 0;
}