HDU 3247-Resource Archiver(AC自动机+BFS+DP)

来源:互联网 发布:上海交通卡充值软件下载 编辑:程序博客网 时间:2024/06/05 16:50

Resource Archiver

Time Limit: 20000/10000 MS (Java/Others)    Memory Limit: 100000/100000 K (Java/Others)
Total Submission(s): 2837    Accepted Submission(s): 909


Problem Description
Great! Your new software is almost finished! The only thing left to do is archiving all your n resource files into a big one.
Wait a minute… you realized that it isn’t as easy as you thought. Think about the virus killers. They’ll find your software suspicious, if your software contains one of the m predefined virus codes. You absolutely don’t want this to happen.
Technically, resource files and virus codes are merely 01 strings. You’ve already convinced yourself that none of the resource strings contain a virus code, but if you make the archive arbitrarily, virus codes can still be found somewhere.
Here comes your task (formally): design a 01 string that contains all your resources (their occurrences can overlap), but none of the virus codes. To make your software smaller in size, the string should be as short as possible.
 

Input
There will be at most 10 test cases, each begins with two integers in a single line: n and m (2 <= n <= 10, 1 <= m <= 1000). The next n lines contain the resources, one in each line. The next m lines contain the virus codes, one in each line. The resources and virus codes are all non-empty 01 strings without spaces inside. Each resource is at most 1000 characters long. The total length of all virus codes is at most 50000. The input ends with n = m = 0.
 

Output
For each test case, print the length of shortest string.
 

Sample Input
2 21110011110110010 0
 

Sample Output
5
 

Source
2009 “NIT Cup” National Invitational Contest
 

Recommend

题意:给你n个合法字符串和m个非法字符串,让你构造一个包含所有的合法字符串(可以重叠),不包含任何一个非法字符串,并且使得这个目标字符串的长度尽可能小,问你最小长度是多少。

题解:一开始的思路是将所有的合法序列的结尾处做一个不同于非法序列的特殊标记,然后定义一个二维的dp数组保存当前的目标字符串匹配到第j个节点且已经过j个所标记的合法结点的最小长度,然后wa上天,想了许久,发现可能走过的结点是已经走过的。。。很难受,于是看了一发正解,大体思路是先用bfs求出所有合法序列末尾结点与其他所有合法状态之间的距离,又因为合法的序列结点很少,故可以用状态压缩的形式保存当前匹配状态(刷了这么多的状压,关键时候还是没想到用他。。。。)之后就知道干什么了,用dp保存一下当前的最小长度即可。

dp[i][j]:当前匹配的合法序列状态为i且以j结点(合法结点)结尾的最小长度。

#include<map>        #include<stack>        #include<queue>        #include<vector>  #include<string>#include<math.h>        #include<stdio.h>        #include<iostream>        #include<string.h>        #include<stdlib.h>        #include<algorithm>        using namespace std;        typedef long long ll;        #define inf 1000000000     #define mod 1000000007         #define  maxn  62005     #define  lowbit(x) (x&-x)        #define  eps 1e-10   int pre[maxn],a[maxn][2],flag[maxn],size,n,m,k,cnt;int d[1025][55],dis[maxn],pnt[55],path[55][55],dp[maxn][60];char s[10500];queue<int>q;void insert(int num)  {      int i,len=strlen(s),now=0,cnt=0;      for(i=0;i<len;i++)      {          int v=s[i]-'0';          if(!a[now][v])          {              flag[size]=0;              memset(a[size],0,sizeof(a[size]));              a[now][v]=size++;          }          now=a[now][v];      }  if(num==-1)flag[now]=-1;  elseflag[now]|=(1<<num);}  void build_fail()  {      int now,i;      for(i=0;i<2;i++)      {          int tmp=a[0][i];          if(tmp)              pre[tmp]=0,q.push(tmp);      }      while(q.empty()==0)      {          now=q.front();          q.pop();          for(i=0;i<2;i++)          {              if(a[now][i]==0)              {                  a[now][i]=a[pre[now]][i];                  continue;              }  int v=pre[now];while(v && a[v][i]==0)v=pre[v];            pre[a[now][i]]=a[v][i];              q.push(a[now][i]);  if(flag[pre[now]]==-1)flag[a[now][i]]=-1;elseflag[a[now][i]]|=flag[pre[now]];        }         }  }void bfs(int x){int i;for(i=0;i<size;i++)dis[i]=inf;q.push(pnt[x]);dis[pnt[x]]=0;while(q.empty()==0){int u=q.front();q.pop();for(i=0;i<2;i++){int v=a[u][i];if(flag[v]<0 || dis[v]!=inf)continue;dis[v]=dis[u]+1;q.push(v);}}for(i=1;i<=cnt;i++)path[x][i]=dis[pnt[i]];}void work(){int i,j,k;for(i=0;i<(1<<n);i++)for(j=1;j<=cnt;j++)dp[i][j]=inf;dp[0][1]=0;for(i=0;i<(1<<n);i++)for(j=1;j<=cnt;j++){if(dp[i][j]==inf)continue;for(k=1;k<=cnt;k++)dp[i|flag[pnt[k]]][k]=min(dp[i|flag[pnt[k]]][k],dp[i][j]+path[j][k]);}int ans=inf;for(i=1;i<=cnt;i++)ans=min(ans,dp[(1<<n)-1][i]);printf("%d\n",ans);}int  main(void)  {      int i;    while(scanf("%d%d",&n,&m)!=EOF)      {  if(m==0 && n==0)break;        size=1;flag[0]=0;cnt=0;         memset(pre,0,sizeof(pre));          memset(a[0],0,sizeof(a[0]));  for(i=0;i<n;i++){scanf("%s",s);insert(i);}for(i=0;i<m;i++){scanf("%s",s);insert(-1);}        build_fail();for(i=0;i<size;i++)if(i==0 || flag[i]>0)pnt[++cnt]=i;for(i=1;i<=cnt;i++)bfs(i);work();}     return 0;  }