【GDOI2016模拟3.15】基因合成

Description:

这里写图片描述

题解:

manacher好像怎么搞都做不出来啊。

考虑回文树。

对于整个串,肯定是找到一个偶回文串,想办法求出构出这个回文串的最小代价。

大概有两种转移:
1.去掉这个回文串的开头和结尾, 由中间的得到。
2.选一个长度小于等于当前回文串长度/2的回文后缀,由它转移而来。

建出回文树,第一个显然很好搞。

第二个可以用倍增,也有 O ( n ) 的方法。

t r a n s [ x ] 表示x往fail链跳,跳到的第一个 l e n <= l e n [ x ] / 2 的点。

t r a n s [ x ] 显然可以由 t r a n s [ f a [ x ] ] 转移而来,这样就 O ( n ) 了。

注意回文树和后缀自动机的区别,跳这个的时候一定要用原串判断,和建的时候一样,不要用树边,不然你会用到之前的回文串,就GG了。

Code:

#include<cstdio>
#include<cstring>
#define fo(i, x, y) for(int i = x; i <= y; i ++)
#define min(a, b) ((a) < (b) ? (a) : (b))
using namespace std;

const int N = 4e5 + 5;

char s[N];
int T, n, n0;
int son[N][4], fail[N], len[N], fa[N], tot, la;

void build() {
    fo(i, 0, tot) memset(son[i], 0, sizeof son[i]);
    tot = 1; la = 0;
    len[1] = -1; fail[0] = 1;
    s[0] = -1;
}

int gf(int x) {
    while(s[n - len[x] - 1] != s[n]) x = fail[x];
    return x;
}

int f[N], trans[N];
int ans;

void add(int c) {
    n ++;
    int cur = gf(la);
    if(!son[cur][c]) {
        int p = ++ tot;
        len[p] = len[cur] + 2;
        fail[p] = son[gf(fail[cur])][c];
        son[cur][c] = p; fa[p] = cur;
    }
    la = son[cur][c];
    if(len[la] & 1) {
        f[la] = len[la];
    } else {
        f[la] = f[fa[la]] + 1;
        int x = trans[fa[la]];
        while(s[n - len[x] - 1] != s[n] || len[x] + 2 > len[la] / 2) x = fail[x];
        if(len[x] + 2 > len[la] / 2) x = 0; else x = son[x][c];
        trans[la] = x;
        f[la] = min(f[la], f[x] + len[la] / 2 - len[x] + 1);
    }
    ans = min(ans, n0 - len[la] + f[la]);
}

int main() {
    for(scanf("%d", &T); T; T --) {
        scanf("%s", s + 1); n = strlen(s + 1);
        fo(i, 1, n) {
            if(s[i] == 'A') s[i] = 0; else
            if(s[i] == 'T') s[i] = 1; else
            if(s[i] == 'C') s[i] = 2; else 
            s[i] = 3;
        }
        f[0] = 1; ans = n; 
        build(); n0 = n; n = 0;
        fo(i, 1, n0) add(s[i]);
        printf("%d\n", ans);
    }
}

猜你喜欢

转载自blog.csdn.net/Cold_Chair/article/details/81121002