【HDU】2457 DNA repair AC自动机+DP

DNA repair

Time Limit: 5000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 1222    Accepted Submission(s): 660


Problem Description
Biologists finally invent techniques of repairing DNA that contains segments causing kinds of inherited diseases. For the sake of simplicity, a DNA is represented as a string containing characters 'A', 'G' , 'C' and 'T'. The repairing techniques are simply to change some characters to eliminate all segments causing diseases. For example, we can repair a DNA "AAGCAG" to "AGGCAC" to eliminate the initial causing disease segments "AAG", "AGC" and "CAG" by changing two characters. Note that the repaired DNA can still contain only characters 'A', 'G', 'C' and 'T'.

You are to help the biologists to repair a DNA by changing least number of characters.
 

Input
The input consists of multiple test cases. Each test case starts with a line containing one integers N (1 ≤ N ≤ 50), which is the number of DNA segments causing inherited diseases.
The following N lines gives N non-empty strings of length not greater than 20 containing only characters in "AGCT", which are the DNA segments causing inherited disease.
The last line of the test case is a non-empty string of length not greater than 1000 containing only characters in "AGCT", which is the DNA to be repaired.

The last test case is followed by a line containing one zeros.
 

Output
For each test case, print a line containing the test case number( beginning with 1) followed by the
number of characters which need to be changed. If it's impossible to repair the given DNA, print -1.
 

Sample Input
   
   
   
   
2 AAA AAG AAAG 2 A TG TGAATG 4 A G C T AGT 0
 

Sample Output
   
   
   
   
Case 1: 1 Case 2: 4 Case 3: -1
 

Source
2008 Asia Hefei Regional Contest Online by USTC

传送门:【HDU】2457 DNA repair

题目大意:
给你n个病毒串以及一个目标串,问至少改变多少的字符能够使得目标串中没有包含任意一个病毒串。

题目分析:
本题通过AC自动机的转移特性构造DP方程:
用dp[ i ][ j ]表示字符串修改完了第 i 个字符到达第 j 个状态时需要改变字符的最小数量。
dp[ i ][ j ] = min { dp[ i ][ pre ] + T ,pre是所有能到达 j 的状态 } ,其中当目标串第 i 个位置与 枚举到的字符相同则T = 0,否则T = 1 。
答案就是min{dp[n][i],n是目标串的长度}。

代码如下:

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

#define REP( I , N ) for ( int I = 0 ; I < N ; ++ I )
#define clear( A , X ) memset ( A , X , sizeof A )

typedef char type_buf ;//buf串类型

const int maxN = 1005 ;//节点总数
const int maxW = 4 ;//分支总数
const int maxQ = 100000 ;//队列大小
const int oo = 0x3f3f3f3f ;

struct Trie {
	int next[maxN][maxW] ;
	int fail[maxN] , end[maxN] ;
	int dp[maxN][maxN] ;
	int Q[maxQ] ;
	int head , tail ;
	int P , root ;
	
	Trie () {}
	
	int New () {
		REP ( i , maxW )
			next[P][i] = -1 ;
		end[P] = 0 ;
		return P ++ ;
	}//New
	
	void Init () {
		P = 0 ;
		root = New () ;
	}//Init
	
	int Get ( type_buf ch ) {
		if ( 'A' == ch ) return 0 ;
		if ( 'C' == ch ) return 1 ;
		if ( 'G' == ch ) return 2 ;
		if ( 'T' == ch ) return 3 ;
	}//Get
	
	void Insert ( type_buf buf[] ) {
		int now = root ;
		for ( int i = 0 ; buf[i] ; ++ i ) {
			int x = Get ( buf[i] ) ;
			if ( next[now][x] == -1 ) next[now][x] = New () ;
			now = next[now][x] ;
		}
		end[now] = 1 ;
	}//Insert
	
	void Build () {
		head = tail = 0 ;
		fail[root] = root ;
		REP ( i , maxW ) {
			if ( next[root][i] == -1 ) {
				next[root][i] = root ;
			}
			else {
				fail[next[root][i]] = root ;
				Q[tail ++] = next[root][i] ;
			}
		}
		while ( head != tail ) {
			int now = Q[head ++] ;
			REP ( i , maxW ) {
				if ( next[now][i] == -1 ) {
					next[now][i] = next[fail[now]][i] ;
				}
				else {
					fail[next[now][i]] = next[fail[now]][i] ;
					end[next[now][i]] |= end[fail[next[now][i]]] ;
					Q[tail ++] = next[now][i] ;
				}
			}
		}
	}//Build
	
	int DP ( type_buf buf[] , int n ) {
		clear ( dp , oo ) ;
		dp[0][0] = 0 ;
		REP ( i , n )
			REP ( j , P )
				REP ( k , maxW ) {
					int Next = next[j][k] ;
					if ( end[Next] == 0 )
						dp[i + 1][Next] = min ( dp[i + 1][Next] , dp[i][j] + ( Get ( buf[i] ) != k ) ) ;
				}
		int ans = oo ;
		REP ( i , P )
			if ( end[i] == 0 )
				ans = min ( ans , dp[n][i] ) ;
		return ans == oo ? -1 : ans ;
	}//DP
} ;

Trie AC ;
type_buf buf[maxN] ;

void Work () {
	int n , cas = 0 ;
	while ( ~scanf ( "%d" , &n ) && n ) {
		AC.Init () ;
		REP ( i , n ) {
			scanf ( "%s" , buf ) ;
			AC.Insert ( buf ) ;
		}
		AC.Build () ;
		scanf ( "%s" , buf ) ;
		int len = strlen ( buf ) ;
		printf ( "Case %d: %d\n" , ++ cas , AC.DP ( buf , len ) ) ;
	}
}//Work

int main () {
	Work () ;
	return 0 ;
}//main


你可能感兴趣的:(数据结构,HDU,AC自动机)