题意:给出n个疾病基因片段,和一个完整基因,求最少修改多少个点能使基因不含疾病片段,
如样例数据:
2 AAA AAG AAAG 2 A TG TGAATG 4 A G C T AGT 0答案就是
Case 1: 1 Case 2: 4 Case 3: -1
意为AAAG中改一个字符就可以不含上面两个片段(任意A改成C或者T)。
如果改不过来(如Case 3)则输出‘-1’。
题解:首先建立AC自动机,然后for循环进行动规(我的代码沙茶了,竟然将他们入队!入队?!!)
动规的状态f[i][j]表示前i个字符跑一遍字典树且得到AC自动机中节点j时无疾病的最小修改字符个数。
如f[1][0]就表示在root上,且目标串第0个字符for循环完毕时的状态。
转移很好想,代码也比较清晰,是以下个字符为ATGC来转移。
其实说起来不是很容易,还是看代码吧。
#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 1010
#define T 4
#define inf 0x3f3f3f3f
using namespace std;
bool end[N];/*是否为一个单词的结尾*/
int fail[N],next[N][T],cnt;/*失败指针、向其他字典树节点的指针、最后一个节点的序号*/
int n,g;
char s[N],tt[25];
int f[N][N]; /* 状态,不赘述了 */
int crs[123];/*一个小小的映射(算是不必要优化吧)*/
queue<int> q;
void init()
{
printf("Case %d: ",++g);
memset(f,0x3f,sizeof(f));
memset(end,0,sizeof(end));
memset(fail,0,sizeof(fail));
memset(next,0,sizeof(next));
cnt=0;f[0][0]=0;/**********注意!**********/
}
void insert()
{
int temp=0,i,alp;
scanf("%s",tt);
for(i=0;tt[i];i++)
{
alp=crs[tt[i]-'A'+'A'];
if(!next[temp][alp])next[temp][alp]=++cnt;
temp=next[temp][alp];
}
end[temp]=1;
}
void acauto()
{/*找fail我就不赘述了*/
while(!q.empty())q.pop();
int temp,i,u,v;
q.push(0);
while(!q.empty())
{
u=q.front();q.pop();
for(i=0;i<T;i++)if(next[u][i])
{
v=next[u][i];
if(!u)fail[v]=0;
else
{
temp=fail[u];
while(temp&&!next[temp][i])temp=fail[temp];
fail[v]=next[temp][i];
end[v]|=end[fail[v]];
/*end的意义在这里已经不是单纯的“单词结尾了” */
/* 而是表示有病与否! */
}
q.push(v);
}
}
}
void dp(int x)
{
int alp,temp,i,u,v;
while(!q.empty())q.pop();
/*这里其实写的很逗,完全没有必要用队列,直接开一个for循环就好了!*/
for(i=0;i<=cnt;i++)q.push(i);
alp=crs[s[x]-'A'+'A'];
while(!q.empty())
{/*直接for循环,写队列反而恶心+很慢*/
u=q.front();q.pop();
if(end[u]||f[x][u]==inf)continue;
for(i=0;i<T;i++)/*以ATGC为下个字符来进行转移*/
{
temp=u;
while(temp&&!next[temp][i])temp=fail[temp];
v=next[temp][i];/*寻找一个节点,使其表示单词长度最大且为当前字串后缀*/
if(end[v])continue;/*疾病基因!直接抛弃该节点!*/
if(i==alp)f[x+1][v]=min(f[x+1][v],f[x][u]); /*两种转移*/
else f[x+1][v]=min(f[x+1][v],f[x][u]+1); /*(根据需不需要修改来决定后面加上的1)*/
}
}
}
int main()
{
int i,mini,len;
crs['A']=0;
crs['T']=1;
crs['G']=2;
crs['C']=3;
/*形成对照,以便于在dp函数和建立字典树时不用多if判断*/
while(scanf("%d",&n),n)
{
init();/*初始化(清数组/赋初值)*/
for(i=1;i<=n;i++)insert();/*建立字典树*/
acauto();/*维护失败指针(fail)*/
scanf("%s",s);
len=strlen(s);
for(i=0,mini=inf;i<len;i++)dp(i);/*动规转移状态*/
for(i=0;i<=cnt;i++)mini=min(mini,f[len][i]);/*找出答案*/
if(mini>len)printf("-1\n");
else printf("%d\n",mini);
}
return 0;
}