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

来源:互联网 发布:网络广告制作软件 编辑:程序博客网 时间:2024/05/01 13:48

题目大意

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

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

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

题解

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

SRC

#include<cstdio>#include<cstdlib>#include<cstring>#include<iostream>#include<algorithm>using namespace std ;#define N 100000 + 10struct 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