欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

jzoj4387 [GDOI2016模拟3.15]基因合成 回文树+dp

程序员文章站 2024-02-12 23:16:22
...

Description


jzoj4387 [GDOI2016模拟3.15]基因合成 回文树+dp

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;
}
相关标签: jzoj