JZOJ4387. 【GDOI2016模拟3.15】基因合成

题目大意

一共有 T 组询问,每组询问一个只包含ACGT的字符串,求通过以下两种操作,最少能得到该串的操作次数。

  • 将当前串翻转后接到原串中
  • 在当前串首或串尾加入一个字符

Data Constraint
对于100%的数据:T10,|S|105

题解

可以注意到操作中有一个翻转,每次操作完以后必然是一个回文串。可以发现目标串必然是由一个回文串(可能为空)再直接首尾添加字符得来。如果我们求出目标串中每一个回文串的最少操作次数,就可以得出答案了。这里可以用回文树来处理。
f[i] 表示回文树上的第 i <script type="math/tex" id="MathJax-Element-488">i</script>个节点的回文串最少需要的操作次数,然后就可以转移了。比较显然的是,奇数长度的回文串并不会对答案造成影响(奇数回文串必能由偶数回文串得来,而且无法翻转得来)。而偶数回文串可以由他的父亲节点增加一对字符得来,这只需要+1次操作(因为可以先添加字符再翻转),或者可以由他的某个长度小于其一半的后缀转移来(语文功底有限所以比较绕 = = 详见程序)
然后直接枚举选择某个回文串,求最小值就是答案。

SRC

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std ;

#define N 100000 + 10
struct Tree {
    int Son[4] ;
    int cost , len , fail ;
} T[N] ;

char S[N] ;
int Tab[N] ;
int Test , Last , L , tot , ans ;

int Find( int now , int half ) {
    while ( T[now].len > half ) now = T[now].fail ;
    return now ;
}

int Getfail( int now ) {
    while ( S[L-T[now].len-1] != S[L] ) now = T[now].fail ;
    return now ;
}

void ADD( int c ) {
    L ++ ;
    int now = Getfail( Last ) ;
    if ( !T[now].Son[c] ) {
        T[++tot].fail = T[Getfail(T[now].fail)].Son[c] ;
        T[now].Son[c] = tot ;
        T[tot].len = T[now].len + 2 ;
        if ( (T[tot].len & 1) == 0 ) {
            int j = Find( T[tot].fail , T[tot].len / 2 ) ;
            T[tot].cost = min( T[now].cost + 1 , T[j].cost + T[tot].len / 2 - T[j].len + 1 ) ;
        } else T[tot].cost = T[tot].len ;
    }
    Last = T[now].Son[c] ;
}

int main() {
    freopen( "gene.in" , "r" , stdin ) ;
    freopen( "gene.out" , "w" , stdout ) ;
    scanf( "%d" , &Test ) ;
    Tab['A'] = 0 , Tab['C'] = 1 , Tab['G'] = 2 , Tab['T'] = 3 ;
    while ( Test -- ) {
        memset( T , 0 , sizeof(T) ) ;
        T[0].fail = T[0].cost = 1 ;
        T[1].len = -1 , T[1].cost = 2 ;
        Last = S[0] = 0 ;
        tot = 1 , L = 0 ;
        scanf( "%s" , S + 1 ) ;
        ans = strlen( S + 1 ) ;
        for (int i = 1 ; i <= ans ; i ++ )
            ADD( Tab[S[i]] ) ;
        for (int i = 2 ; i <= tot ; i ++ ) ans = min( ans , T[i].cost + L - T[i].len ) ;
        printf( "%d\n" , ans ) ;
    }
    return 0 ;
}

以上.

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值