题意:给出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; }