【GDOI2016模拟3.15】基因合成

Description:

这里写图片描述

题解:

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

考虑回文树。

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

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

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

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

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

trans[x] t r a n s [ x ] 显然可以由 trans[fa[x]] t r a n s [ f a [ x ] ] 转移而来,这样就 O(n) 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);
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值