題目鏈接:點擊查看
題目大意:給出 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;
}