题目链接:点击查看
题目大意:给出 n 个目标串和 m 个病毒串,要求构造出一个长度最短的,且包含全部 n 个目标串,但是不能包含任意一个病毒串的01字符串,输出其最短长度
题目分析:比较综合的一道题目了,以为是涉及到目标串和病毒串之间的关系,所以读完题目后不难想到先将这些字符串都扔到AC自动机里去,因为 n 非常小,所以可以考虑状态压缩,于是最初的想法就是壮压dp直接转移,dp[ state1 ][ state2 ] 代表着含有目标串状态为state1,且在AC自动机中的状态为state2时的最短长度,但非常遗憾的是,整个dp转移的时间复杂度达到了1024*50000*2,不出意外的话是会TLE的,所以我们必须想办法优化,稍微想一下的话不难发现,AC自动机中绝大部分的字符串都是病毒串的,也就是没有作用的结点,我们真正会用到的也只有那 n<=10 个结点,所以在建好AC自动机后,将可以代表目标串的结点单独拿出,因为之前的转移方程是:(设 i 为目标串的状态,j 为AC自动机的状态,nj 为接下来的状态,id[ i ]为AC自动机内结点 i 所代表的目标串状态)
dp[ i | id[ nj ] ][ nj ] = min( dp[ i | id[ nj ] ][ nj ] , dp[ i ][ j ] + 1 )
而现在的状态转移就变成了:
dp[ i | id[ nj ] ][ nj ] = min( dp[ i | id[ nj ] ][ nj ] , dp[ i ][ j ] + dis[ i ][ nj ] )
其中dis[ i ][ j ]代表着从状态 i 到状态 j 所需要添加的最少字符,到这里,我们就可以发现,因为dis[ i ][ j ]可以用bfs预处理得出,这样总的时间复杂度也就下降为了 1024*10*10 了
代码:
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<unordered_map>
using namespace std;
typedef long long LL;
typedef unsigned long long ull;
const int inf=0x3f3f3f3f;
const int N=1e5+100;
char s[N];
int fail[N],id[N],trie[N][2],cnt;
void insert_word(int _id)
{
int len=strlen(s);
int pos=0;
for(int i=0;i<len;i++)
{
int to=s[i]-'0';
if(!trie[pos][to])
trie[pos][to]=++cnt;
pos=trie[pos][to];
}
id[pos]=_id;
}
void getfail()
{
queue<int>q;
for(int i=0;i<2;i++)
{
if(trie[0][i])
{
fail[trie[0][i]]=0;
q.push(trie[0][i]);
}
}
while(!q.empty())
{
int cur=q.front();
q.pop();
if(id[fail[cur]]<0)
id[cur]=-1;
else
id[cur]|=id[fail[cur]];
for(int i=0;i<2;i++)
{
if(trie[cur][i])
{
fail[trie[cur][i]]=trie[fail[cur]][i];
q.push(trie[cur][i]);
}
else
trie[cur][i]=trie[fail[cur]][i];
}
}
}
vector<int>state;
int dis[N],maze[15][15],dp[(1<<10)+100][15];
void bfs(int pos)
{
memset(dis,inf,sizeof(dis));
queue<int>q;
q.push(state[pos]);
dis[state[pos]]=0;
while(q.size())
{
int cur=q.front();
q.pop();
for(int i=0;i<2;i++)
{
int nj=trie[cur][i];
if(id[nj]<0)
continue;
if(dis[nj]>dis[cur]+1)
{
dis[nj]=dis[cur]+1;
q.push(nj);
}
}
}
for(int i=0;i<state.size();i++)
maze[pos][i]=dis[state[i]];
}
void init()
{
cnt=0;
memset(id,0,sizeof(id));
memset(trie,0,sizeof(trie));
state.clear();
}
int main()
{
#ifndef ONLINE_JUDGE
// freopen("input.txt","r",stdin);
// freopen("output.txt","w",stdout);
#endif
// ios::sync_with_stdio(false);
int n,m;
while(scanf("%d%d",&n,&m)!=EOF&&n+m)
{
init();
for(int i=0;i<n;i++)
{
scanf("%s",s);
insert_word((1<<i));
}
while(m--)
{
scanf("%s",s);
insert_word(-1);
}
getfail();
state.push_back(0);
for(int i=0;i<=cnt;i++)
if(id[i]>0)
state.push_back(i);
for(int i=0;i<state.size();i++)
bfs(i);
memset(dp,inf,sizeof(dp));
dp[0][0]=0;
for(int i=0;i<(1<<n);i++)
{
for(int j=0;j<state.size();j++)
{
for(int k=0;k<state.size();k++)
{
if(dp[i][j]!=inf)
{
if(maze[j][k]==inf)
continue;
if(j==k)
continue;
dp[i|id[state[k]]][k]=min(dp[i|id[state[k]]][k],dp[i][j]+maze[j][k]);
}
}
}
}
int ans=inf;
for(int i=0;i<state.size();i++)
ans=min(ans,dp[(1<<n)-1][i]);
printf("%d\n",ans);
}
return 0;
}