传送门:【codeforces】2014-2015ACM-ICPC CERC 14 Problem G: Virus synthesis
题目分析:首先我们构造回文树,然后我们只考虑长度为偶数的回文串,长度为奇数的回文串总可以通过长度为偶数的回文串然后加上剩下的所需字符构成(当然这个偶数回文串的长度可以是0,此时这个奇数回文串的构造次数就是其长度)。对于一个偶数长度回文串u,假设其构造次数为dp[u],由这个串在两边加上同一个字符可以构造出一个长度等于len[u]+2的回文串v,则dp[v] = min { dp[u] + 1,len[v]/2-len[subfail[v]]+dp[subfail[v]]+1
}。
dp[v]=dp[u]+1的意思是,由回文串的折半串在一边加上一个字符再镜像一次得到,subfail是一个指向一个小于等于回文串v长度一半的最长的以回文串v最右端字符为结尾的回文串的指针(用不同颜色区分了一些,千万别被我绕晕了T T)。subfail可以在构造回文树的同时求出,和fail指针的求法类似。
对于len[v]/2-len[subfail[v]]+dp[subfail[v]]+1,我们设v回文串长度的一般减去subfail[v]指向的回文串的长度为y,subfail[v]指向的回文串的长度的一半为x+1,对照下图我们可以知道,串v的dp值可以由串u的值-1(取消折半)+dp[subfail[v]](加上小于等于一半的最长回文串的dp值)+1(加上一个字符)+y(加上到回文串折半位置所需的字符数)+1(镜像)得到。因为这个值在之前是没有被计算的,所以需要和第一种情况间取一个最小值,来确定回文串v的构造所需步数的dp值。
代码如下:
#include <cstdio> #include <cstring> #include <algorithm> using namespace std ; typedef long long LL ; #define rep( i , a , b ) for ( int i = ( a ) ; i < ( b ) ; ++ i ) #define For( i , a , b ) for ( int i = ( a ) ; i <= ( b ) ; ++ i ) #define rev( i , a , b ) for ( int i = ( a ) ; i >= ( b ) ; -- i ) #define clr( a , x ) memset ( a , x , sizeof a ) #define lson l , m #define rson m + 1 , r #define mid ( ( l + r ) >> 1 ) const int MAXN = 100005 ; const int N = 4 ; const int INF = 0x3f3f3f3f ; struct Palindromic_Tree { int next[MAXN][N] ; int fail[MAXN] ; int subfail[MAXN] ; int len[MAXN] ; int cnt[MAXN] ; int num[MAXN] ; int S[MAXN] ; int last ; int n ; int p ; int newnode ( int l ) { rep ( i , 0 , N ) next[p][i] = 0 ; cnt[p] = 0 ; num[p] = 0 ; len[p] = l ; return p ++ ; } void init () { p = 0 ; newnode ( 0 ) ; newnode ( -1 ) ; last = 0 ; n = 0 ; S[n] = -1 ; fail[0] = 1 ; } int get_fail ( int x ) { while ( S[n - len[x] - 1] != S[n] ) x = fail[x] ; return x ; } int encode ( int c ) { if ( c == 'A' ) return 0 ; if ( c == 'C' ) return 1 ; if ( c == 'G' ) return 2 ; if ( c == 'T' ) return 3 ; } void add ( int c ) { c = encode ( c ) ; S[++ n] = c ; int cur = get_fail ( last ) ; if ( !next[cur][c] ) { int now = newnode ( len[cur] + 2 ) ; fail[now] = next[get_fail ( fail[cur] )][c] ; next[cur][c] = now ; num[now] = num[cur] + 1 ; if ( len[now] <= 2 ) subfail[now] = fail[now] ; else { int x = subfail[cur] ; while ( S[n - len[x] - 1] != S[n] || 2 * ( len[x] + 2 ) > len[now] ) x = fail[x] ; subfail[now] = next[x][c] ; } //printf ( "%d %d\n" , len[now] , len[subfail[now]] ) ; } last = next[cur][c] ; cnt[last] ++ ; } void count () { rev ( i , p - 1 , 0 ) cnt[fail[i]] += cnt[i] ; } } ; Palindromic_Tree T ; char s[MAXN] ; int Q[MAXN] , head , tail ; int dp[MAXN] ; int n ; void bfs ( int src ) { int ans = n ; head = tail = 0 ; Q[tail ++] = src ; dp[src] = 1 ; while ( head != tail ) { int u = Q[head ++] ; rep ( i , 0 , N ) { int v = T.next[u][i] ; if ( !v ) continue ; int tmp = T.len[v] / 2 - T.len[T.subfail[v]] + ( T.len[T.subfail[v]] % 2 ? T.len[T.subfail[v]] : dp[T.subfail[v]] ) + 1 ; dp[v] = min ( dp[u] + 1 , tmp ) ; ans = min ( ans , n - T.len[v] + dp[v] ) ; Q[tail ++] = v ; //printf ( "%d %d %d %d\n" , T.len[v] , T.len[T.subfail[v]] , dp[v] , dp[T.subfail[v]] ) ; } } printf ( "%d\n" , ans ) ; } void solve () { T.init () ; scanf ( "%s" , s ) ; n = strlen ( s ) ; rep ( i , 0 , n ) T.add ( s[i] ) ; bfs ( 0 ) ; } int main () { int T ; scanf ( "%d" , &T ) ; while ( T -- ) solve () ; return 0 ; }