收藏 分销(赏)

AC自动机上的DP.doc

上传人:xrp****65 文档编号:6270737 上传时间:2024-12-04 格式:DOC 页数:10 大小:79KB 下载积分:10 金币
下载 相关 举报
AC自动机上的DP.doc_第1页
第1页 / 共10页
AC自动机上的DP.doc_第2页
第2页 / 共10页


点击查看更多>>
资源描述
[ADN.cn][Library]Summary 动态规划总结 by Amber 1. Pku 3691 DNA repair   题意为给你一些病毒串,然后给你一个目标串。问你如何修改目标串,使得目标串中不含任何病毒串,且使修改量最少。我们把所有病毒串构成AC自动机,如果 Trie 图中某结点是病毒串的结尾,标记该结点为危险结点。对于某一结点 p,如果他的失败指针所指向的结点为危险结点,则 p 也将标记为危险结点。这时我们沿着AC自动机上走,只要不走过危险结点,最后得到的任何串必然不包含病毒串。这时原问题变为如何在 AC自动机上走(不走过危险结点),使得走过的路径得到的串与目标串匹配最多,也就是不匹配量最小,修改量最少。我们可以用动态规划解决。用 dp[i][j] 表示走了 i 步到达 Trie 图上的 j 结点时,所走过的路径构成的串与目标串的最小不匹配字符量。则可以得到转移方程 dp[i][ son[j] ]= min{ dp[i][ son[j] ], dp[i-1][j]+ (tran[j][ son[j] ]!= str[i] ) } son[j] 表示结点 j 的孩子结点,tran[j][ son[j] ]表示 j 结点到 son[j] 结点所经过的路径上的字母,str[i] 表示目标串的 i 个字符。代码: PKU 3691 #include <stdio.h> #include <string.h> int const N= 1010, inf= 1000000; #define min(a,b) ((a)<(b)?(a):(b)) struct Trie{     int fail, flag;     int next[4];     void init(){         fail= -1; flag= 0;         for( int i= 0; i< 4; ++i ) next[i]= 0;     } }tb[N]; int idx= 0, dp[N][N], que[N], n, m; char str[N]; inline int toInt( char ch ){     switch( ch ){         case 'A': return 0;         case 'T': return 1;         case 'G': return 2;         case 'C': return 3;     } } void inline insert( char* s ){     int rt= 0;     while( *s ){         int t= toInt( *s );         if( !tb[rt].next[t] ){             tb[++idx].init();             tb[rt].next[t]= idx;         }         rt= tb[rt].next[t]; s++;     }     tb[rt].flag= 1; } void bfs(){     int head= 0, tail= 0; que[0]= 0;     while( head<= tail ){         int now= que[head++];         for( int t= 0; t< 4; ++t )         if( tb[now].next[t] ){             int p= tb[now].next[t], q= tb[now].fail;             while( q!= -1 && !tb[q].next[t] ) q= tb[q].fail;             if( q== -1 ) tb[p].fail= 0;             else {                 tb[p].fail= tb[q].next[t];                 tb[p].flag= tb[p].flag| tb[ tb[p].fail ].flag;                 // 传遍病毒串              }             que[++tail]= p;         }     } } int solve(){     m= strlen( str );     for( int i= 0; i<= idx; ++i )     for( int j= 0; j<= m; ++j ) dp[j][i]= inf;     dp[0][0]= 0;          for( int i= 1; i<= m; ++i ){         for( int j= 0; j<= idx; ++j )         if( dp[i-1][j]!= inf ){             for( int k= 0; k< 4; ++k ){                 int p= tb[j].next[k];                                  if( p!= 0 && !tb[p].flag )                 dp[i][p]= min( dp[i][p], dp[i-1][j]+ ( toInt( str[i-1] )!= k ) );                 else if( p== 0 ){                     int f= tb[j].fail;                     while( f!= -1 && !tb[f].next[k] ) f= tb[f].fail;                     if( f== -1 ) p= 0;                     else p= tb[f].next[k];                     if( !tb[p].flag )                     dp[i][p]= min( dp[i][p], dp[i-1][j]+ ( toInt( str[i-1] )!= k ) );                  }             }         }     }     int ans= inf;     for( int i= 0; i<= idx; ++i )     ans= min( ans, dp[m][i] );          if( ans== inf ) return -1;     return ans; } int main(){     int test= 1;     while( scanf("%d",&n)!= EOF ){         if( n== 0 ) return 0;          tb[0].init(); idx= 0;         for( int i= 0; i< n; ++i ){             scanf("%s", str );             insert(str); }         scanf("%s",str );         bfs();         printf("Case %d: %d\n", test++, solve() );     }     return 0; }   2. Tju 3220 Ring 题意为给你一些单词,每个单词有一个value 值。要你找一个长度不超过 m 的串,使得串中所包含的单词的 value 和最大。同样我们先把单词构建成 Trie 图。在 Trie 图上,如果单词  a  是单词 b 的后缀,则单词 b 的 value 得加上单词 a 的 value。再如果结点 p 的失败指针为 q,如果  p 的孩子 p->child[t] 为空而 q->child[t] 不为空,这时我们将 p->child[t] 的值赋为 q->child[t],这样做的好处就是在 trie 图上走的时候不用考虑失败指针了。做完这些就可以 DP 了。我们定义 dp[i][j] 为构建的字符串长度为 i 同时到达 Trie 图上的 j 结点时字符串的最大 value 值,这时就有 dp[i][p]= max{ dp[i][p], dp[i][j]+ value[p] }  p 表示 j 的孩子结点,方程不难理解。题目要求出一个字典序最小的串,在 DP 过程中还得不断更新这个串。写了一个很烂的代码: 代码 #include <stdio.h> #include <stdlib.h> #include <string.h> int const N= 5010, M= 110; int n, m, h[N], cnt, dp[M][N], path[M][N], ch[M][N], que[N]; int xx, yy, num= 0; struct Trie{     int idx, fail;     int next[26];     void init(){         fail= -1; idx= 0;         for( int i= 0; i< 26; ++i )         next[i]= 0;     } }tb[N]; char str[M], res[M]; void insert( char* s, int d ){     int rt= 0;     while( *s ){         int t= *s- 'a';         if( !tb[rt].next[t] ){             tb[++cnt].init();             tb[rt].next[t]= cnt;         }         rt= tb[rt].next[t]; s++;     }     tb[rt].idx= d; } void bfs(){     int head= 0, tail= 0; que[0]= 0;     while( head<= tail ){         int now= que[head++];         for( int t= 0; t< 26; ++t )         if( tb[now].next[t] ){             int p= tb[now].next[t], q= tb[now].fail;             while( q!= -1 && !tb[q].next[t] ) q= tb[q].fail;             if( q== -1 ) tb[p].fail= 0;             else {                 tb[p].fail= tb[q].next[t];                 if( !tb[p].idx && tb[ tb[p].fail ].idx ){                     tb[p].idx= tb[ tb[p].fail ].idx; }                 else if( tb[p].idx && tb[ tb[p].fail ].idx ){                     h[ tb[p].idx ]+= h[ tb[ tb[p].fail ].idx ] ;                 }             }             que[++tail]= p;         }else{             int q= tb[now].fail;             while( q!= -1 && !tb[q].next[t] ) q= tb[q].fail;             if( q>= 0 ) tb[now].next[t]= tb[q].next[t];         }     } } void print( int x, int y ){     if( path[x][y]== -1 ) return;     print( x- 1, path[x][y] );     str[num++]= ch[x][y]+ 'a'; } int solve(){     for( int i= 0; i<= n; ++i )     for( int j= 0; j<= cnt; ++j ) {         dp[i][j]= -1; path[i][j]= -1; ch[i][j]= -1; }          dp[0][0]= 0;     for( int i= 1; i<= n; ++i ){         for( int j= 0; j<= cnt; ++j )         if( dp[i-1][j]!= -1 ){             for( int k= 0; k< 26; ++k )             if( tb[j].next[k] ){                 int p= tb[j].next[k];                  if( dp[i][p]< dp[i-1][j]+ h[ tb[p].idx ] ){                     dp[i][p]= dp[i-1][j]+ h[ tb[p].idx ];                     path[i][p]= j; ch[i][p]= k;                 }else if( dp[i][p]== dp[i-1][j]+ h[ tb[p].idx ] ){                     char s1[M], s2[M];                     print( i, p ); str[num]= 0; num= 0; strcpy( s1, str );                                          int ox= path[i][p], oy= ch[i][p];                     path[i][p]= j, ch[i][p]= k;                                          print( i, p ); str[num]= 0; num= 0; strcpy( s2, str );                     if( strcmp( s2, s1 )< 0 ) continue;                     else path[i][p]= ox; ch[i][p]= oy;                 }             }         }     }     int ans= 0;     for( int i= 1; i<= n; ++i )     for( int j= 0; j<= cnt; ++j )     if( dp[i][j]> ans ){         ans= dp[i][j]; xx= i,  yy= j;     }     int flag= 0;     for( int j= 0; j<= cnt; ++j )     if( dp[xx][j]== ans ){         num= 0; print(xx,j); str[num]= 0;                  if( !flag){             strcpy( res, str );             flag= 1;         }else if( strcmp( str, res )< 0 ){             strcpy( res, str );         }     }     return ans; } int main(){     int test;          scanf("%d",&test );     while( test-- ){         scanf("%d%d",&n,&m );         cnt= 0; tb[0].init();         for( int i= 1; i<= m; ++i ){             scanf("%s",str );             insert(str, i);         }         for( int i= 1; i<= m; ++i ) scanf("%d", h+ i );         bfs();         int ans= solve();                  if( ans== 0 ) puts("");         else puts(res);     }     return 0; }   3. HDU 3341 Lost's revenge 解题报告:自动机+DP。设主串有na个A、nc个C、ng个G、nt个T,于是题意转化为用na个A、nc个C、ng个G、nt个T生成一个新串,使模式串匹配次数最大。先将模式串生成自动机〔trie图〕,然后在这上面进行DP,状态可表示为dp[d,s],d为自动机状态,s表示由ma个A、mc个C、mg个G、mt个T的生成串〔s可表示为ma*(nc+1)*(ng+1)*(nt+1)+mc*(ng+1)*(nt+1)+mg*(nt+1)+mt〕,转移为O(4),总复杂度O(500*11^4*4)。摘自: 代码: 代码 #include <stdio.h> #include <stdlib.h> #include <string.h> #define max(a,b) ( (a)> (b)? (a): (b) ) struct Trie{     int flag, fail;     int next[4];     void init(){         flag= 0; fail= -1;         for( int i= 0; i<4 ; ++i )         next[i]= 0;     } }tb[600]; int n, idx; char str[100]; inline int Ch( char ch ){     switch( ch ){         case 'A': return 0;         case 'T': return 1;         case 'G': return 2;         case 'C': return 3;     } } void insert( char* s ){     int rt= 0;     while( *s ){         int t= Ch(*s);         if( tb[rt].next[t]== 0 ){             tb[++idx].init();             tb[rt].next[t]= idx;         }         rt= tb[rt].next[t]; s++;     }     tb[rt].flag++; } int que[20010], head, tail, vis[20010]; void bfs(){     head= 0, tail= 0, que[0]= 0;     while( head<= tail ){         int now= que[head++];         for( int t= 0; t< 4; ++t )         if( tb[now].next[t] ){             int p= tb[now].next[t], q= tb[now].fail;             while( q!= -1 && !tb[q].next[t] ) q= tb[q].fail;             if( q== -1 ) tb[p].fail= 0;             else{                 tb[p].fail= tb[q].next[t];                 if( tb[ tb[p].fail ].flag ) tb[p].flag+= tb[ tb[p].fail ].flag;             }             que[++tail]= p;         }         else{             int q= tb[now].fail;             while( q!= -1 && !tb[q].next[t] ) q= tb[q].fail;             if( q!= -1 ) tb[now].next[t]= tb[q].next[t];         }     } } int dp[20000][510], num[4], base[4]; char SS[4]= { 'A', 'T', 'G', 'C' }; void inline get_num( int state, int atgc[4] ){     for( int i= 0; i< 4; ++i ){         atgc[i]= state/ base[i];         state%= base[i];     } } int inline new_state( int atgc[4], int k ){     int res= 0;     for( int i= 0; i< 4; ++i )     if( i!= k ) res+= base[i]* atgc[i];     else res+= base[i]* ( atgc[i]+ 1 );          return res; } int solve(){     int len= strlen(str);     for( int i= 0; i< 4; ++i ) num[i]= 0;     for( int i= 0; i< len; ++i ) num[ Ch(str[i]) ]++;          int tot= 0;     for( int i= 0; i< 4; ++i ){         int t= 1;         for( int j= i+ 1; j< 4; ++j ) t*= ( num[j]+ 1 );         base[i]= t; tot+= base[i]* num[i];     }     int head= 0, tail= 0, size= 1;     for( int i= 0; i<= tot; ++i )     for( int j= 0; j<= idx; ++j ) dp[i][j]= -1;          for( int i= 0; i<= tot; ++i ) vis[i]= 0; vis[0]= 1;          que[0]= 0; dp[0][0]= 0;     while( head<= tail ){         int ts= 0;         for( int i= 0; i< size; ++i ){             int state= que[head++], atgc[4];             vis[state]= 0;                          if( state== tot ) continue;             get_num( state, atgc );                          for( int p= 0; p<= idx; ++p )             if( dp[state][p]!= -1 ){                 for( int k= 0; k< 4; ++k )                 if( atgc[k]+ 1<= num[k] ){                     int newState= new_state( atgc, k );                     int q= tb[p].next[k];                                          if( dp[state][p]+ tb[q].flag> dp[newState][q] ){                         dp[newState][q]= dp[state][p]+ tb[q].flag;                         if( !vis[newState] ){                             que[++tail]= newState; ts++;                             vis[newState]= 1;                         }                     }                 }             }         }         size= ts;     }          int ans= 0;     for( int i= 0; i<= idx; ++i )     ans= max( ans, dp[tot][i] );          return ans; } int main(){     int test= 1;     while( scanf("%d",&n)!= EOF ){         if( n== 0 ) return 0;         idx= 0; tb[0].init();                  for( int i= 0; i< n; ++i ){             scanf("%s", str );             insert( str );         }         scanf("%s", str );         bfs();         printf("Case %d: %d\n", test++, solve() );     }     return 0; }   10 第 10 页 共 10 页
展开阅读全文

开通  VIP会员、SVIP会员  优惠大
下载10份以上建议开通VIP会员
下载20份以上建议开通SVIP会员


开通VIP      成为共赢上传

当前位置:首页 > 包罗万象 > 大杂烩

移动网页_全站_页脚广告1

关于我们      便捷服务       自信AI       AI导航        抽奖活动

©2010-2026 宁波自信网络信息技术有限公司  版权所有

客服电话:0574-28810668  投诉电话:18658249818

gongan.png浙公网安备33021202000488号   

icp.png浙ICP备2021020529号-1  |  浙B2-20240490  

关注我们 :微信公众号    抖音    微博    LOFTER 

客服