题意:
给出n个资源串,m个病毒串,现在要如何连接资源串使得不含病毒串(可以重叠)。
题解:
真心不会做。
这题注意点好多,稍不留意就wa。
看了题解我是这样理解的,我么可以把资源串和病毒串都放到ac机上,这样的做是为了把病毒串挑掉。在建ac机时,通过判断fail指针对应节点是否是病毒串,然后将fail指针的内容根性到这个节点。同理对于资源串也用相同方法。我们这样设置标记,病毒串ID=-1,资源串ID=对应状态(比如编号是i那么ID=1<<i)。后面建ac机可以整合这些状态。接着还要BFS得到剔除病毒串后可以用资源串到各个资源串的距离,这个方法很巧秒。之后就可以状压了dp[s][i]状态为s,ac机上节点i对应的最小长度。i不一定要在s中,因为可以通过多个部分整合成一个部分。其实建ac机时就将包含关系的资源串整合成了一个串,操作很巧妙的。
#include<iostream>
#include<math.h>
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<vector>
#include<queue>
#include<map>
#include<set>
using namespace std;
#define B(x) (1<<(x))
typedef long long ll;
const int oo=0x3f3f3f3f;
const ll OO=1LL<<61;
const ll MOD=20090717;
const int maxn=1005;
const int SIZE=60005;
const int alph=2;
char str[maxn];
int dp[B(10)+5][11];
int pos[11];
int dis[SIZE];
int maze[11][11];
struct AC
{
int next[SIZE][alph],fail[SIZE],ID[SIZE];
int root,cnt;
void Init()
{
cnt=0;
root=newNode();
}
int newNode()
{
for(int i=0;i<alph;i++)
next[cnt][i]=-1;
ID[cnt++]=0;
return cnt-1;
}
void Insert(char buff[],int id)
{
int now=root;
int len=strlen(buff);
for(int i=0,k;i<len;i++)
{
k=buff[i]-'0';
if(next[now][k]==-1)
next[now][k]=newNode();
now=next[now][k];
}
ID[now]=id;
}
void build()
{
queue<int>Q;
fail[root]=root;
int now=root;
for(int i=0;i<alph;i++)
{
if(next[now][i]==-1)
next[now][i]=root;
else
{
fail[next[now][i]]=root;
Q.push(next[now][i]);
}
}
while(!Q.empty())
{
now=Q.front();
Q.pop();
if(ID[fail[now]]==-1) ID[now]=-1;///和病毒串一样因此要改成-1,这样就相当于删除了病毒串
else ID[now]|=ID[fail[now]];
for(int i=0;i<alph;i++)
{
if(next[now][i]==-1)
next[now][i]=next[fail[now]][i];
else
{
fail[next[now][i]]=next[fail[now]][i];
Q.push(next[now][i]);
}
}
}
}
void cmin(int& a,int b)
{
if(a==-1) a=b;
else if(b<a) a=b;
}
void bfs(int s,int n)
{
queue<int>Q;
memset(dis,-1,sizeof dis);
dis[pos[s]]=0;
Q.push(pos[s]);
while(!Q.empty())
{
int u=Q.front();
Q.pop();
for(int i=0;i<2;i++)
{
int v=next[u][i];
if(dis[v]==-1&&ID[v]!=-1)///因此只要处理到邻接的点即可,并不是多个点的路径
{
dis[v]=dis[u]+1;
Q.push(v);
}
}
}
for(int i=0;i<n;i++)
maze[s][i]=dis[pos[i]];
}
int DP(int n)
{
int nCnt;
memset(maze,-1,sizeof maze);
pos[0]=0;
nCnt=1;
for(int i=0;i<cnt;i++) if(ID[i]>0) pos[nCnt++]=i;
for(int i=0;i<nCnt;i++)
bfs(i,nCnt);
memset(dp,0x3f,sizeof dp);
dp[0][0]=0;
int full=B(n)-1;
for(int s=0;s<=full;s++)
{
for(int i=0;i<nCnt;i++)
if(dp[s][i]!=oo)
{
//if(!(s&B(i)))continue;
for(int j=0;j<nCnt;j++)
if(maze[i][j]!=-1&&i!=j)
{
int st=s|ID[pos[j]];
cmin(dp[st][j],dp[s][i]+maze[i][j]);
}
}
}
int ans=oo;
for(int i=0;i<nCnt;i++)
cmin(ans,dp[full][i]);
return ans;
}
};
AC ac;
int main()
{
int n,m;
while(scanf("%d %d",&n,&m)!=EOF)
{
if(n==0&&m==0)break;
ac.Init();
for(int i=0;i<n;i++)
{
scanf("%s",str);
ac.Insert(str,B(i));
}
for(int i=0;i<m;i++)
{
scanf("%s",str);
ac.Insert(str,-1);
}
ac.build();
cout<<ac.DP(n)<<endl;
}
return 0;
}
/**
5 2
01000111000001110001
11100000111000110011
00011100011001111011
10001100111101100010
10011110110001001111
10001110000011100011
11000110011110110001
ans=66
*/