DNA repair


Time Limit: 2000MS

 

Memory Limit: 65536K

Total Submissions: 6966

 

Accepted: 3231


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​



【分析】

比较经典的AC自动机+dp

题意为给你一些病毒串,然后给你一个目标串。
问你如何修改目标串,使得目标串中不含任何病毒串,且使修改量最少。
我们把所有病毒串构成AC自动机,如果 Trie 图中某结点是病毒串的结尾,标记该结点为危险结点。
对于某一结点 p,如果他的失败指针所指向的结点为危险结点,则 p 也将标记为危险结点。
这时我们沿着AC自动机上走,只要不走过危险结点,最后得到的任何串必然不包含病毒串。
这时原问题变为如何在 AC自动机上走(不走过危险结点),使得走过的路径得到的串与目标串匹配最多,也就是不匹配量最小,修改量最少。
我们可以用动态规划解决。
用 dp[i][j] 表示走了 i 步到达 Trie 图上的 j 结点时,所走过的路径构成的串与目标串的最小不匹配字符量。



【代码】

#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#define ll long long
#define M(a) memset(a,0,sizeof a)
#define fo(i,j,k) for(i=j;i<=k;i++)
using namespace std;
const int mxn=1005;
char s[mxn];
queue <int> q;
int n,m,T,num;
int dp[mxn][mxn],map[150];
struct node {int son[5],fail,cnt;} a[mxn];
inline void clear(int x)
{
a[x].cnt=a[x].fail=0;
memset(a[x].son,0,sizeof a[x].son);
}
inline void trie()
{
scanf("%s",s+1);
int i,j,x=0,len=strlen(s+1);
fo(i,1,len)
{
int c=map[s[i]];
if(!a[x].son[c])
clear(++num),a[x].son[c]=num;
x=a[x].son[c];
}
a[x].cnt=1;
}
inline void build()
{
int i,j;
fo(i,1,4) if(a[0].son[i]) q.push(a[0].son[i]);
while(!q.empty())
{
int x=q.front();q.pop();
int fail=a[x].fail;
fo(i,1,4)
{
int y=a[x].son[i];
if(y) a[y].fail=a[fail].son[i],q.push(y);
else a[x].son[i]=a[fail].son[i];
}
a[x].cnt|=a[fail].cnt;
}
}
inline int dynamic()
{
int i,j,k,ans;
scanf("%s",s+1);
int len=strlen(s+1);
memset(dp,0x3f,sizeof dp);
ans=dp[0][0],dp[0][0]=0;
fo(i,1,len)
fo(j,0,num) if(!a[j].cnt)
fo(k,1,4)
dp[i][a[j].son[k]]=min(dp[i][a[j].son[k]],dp[i-1][j]+(k!=map[s[i]]));
fo(j,0,num) if(!a[j].cnt) ans=min(ans,dp[len][j]);
return ans<=len?ans:-1;
}
int main()
{
int i,j;
map['A']=1,map['G']=2,map['C']=3,map['T']=4;
while(scanf("%d",&n) && n)
{
T++,num=0,clear(0);
fo(i,1,n) trie();
build();
printf("Case %d: %d\n",T,dynamic());
}
return 0;
}