2 2 1110 0111 101 1001 0 0
5
题意:给你n个01串和m个01串,让你构造一个最短的字符串要包含前n个,但不能包含m中的任意一个。
思路:先把(n+m)个串都放进trie图中,那么n(n<=10)个节点是否已经经过的状态可以用二进制表示出来,然后把m个串的尾节点都设为危险节点,然后进行dp。容易想到的dp是用dp[j][state]表示走到第j个节点,当前n个串的储存情况为state所需要的最少步数,但是这样的时间复杂度是60000*1024*10超时了。所以我们会发现其实在dp过程中,很多节点都是没有用的,即对n个点状态没有影响,只有到n个字符串尾节点对应的trie图节点,state状态才会改变,所以我们考虑先在根节点,n个尾节点在trie图中对应的节点两两之间求最短路即最少步数,然后就在这n个点中状压dp就行了。
#include<iostream> #include<stdio.h> #include<stdlib.h> #include<string.h> #include<math.h> #include<vector> #include<map> #include<set> #include<queue> #include<stack> #include<string> #include<algorithm> using namespace std; typedef long long ll; #define inf 99999999 #define pi acos(-1.0) #define maxnode 60050 int dp[20][1030],dist[20][20],vis[maxnode]; int n,m; map<int,int>jiedianhao; map<int,int>chuanhao; map<int,int>::iterator it; struct trie{ int sz,root,val[maxnode],next[maxnode][2],fail[maxnode]; int q[1111111],q1[1111111][2]; void init(){ int i; sz=root=0; val[0]=0; for(i=0;i<2;i++){ next[root][i]=-1; } jiedianhao[0]=0; chuanhao[0]=0; } int idx(char c){ return c-'0'; } void charu(char *s,int num){ int i,j,u=0; int len=strlen(s); for(i=0;i<len;i++){ int c=idx(s[i]); if(next[u][c]==-1){ sz++; val[sz]=0; next[u][c]=sz; u=next[u][c]; for(j=0;j<2;j++){ next[u][j]=-1; } } else{ u=next[u][c]; } } if(num==-1)val[u]=-1; else{ jiedianhao[num]=u; chuanhao[u]=num; val[u]|=(1<<num-1); } } void build(){ int i,j; int front,rear; front=1;rear=0; for(i=0;i<2;i++){ if(next[root][i]==-1 ){ next[root][i]=root; } else{ fail[next[root][i] ]=root; rear++; q[rear]=next[root][i]; } } while(front<=rear){ int x=q[front]; if(val[fail[x] ]==-1 )val[x]=-1; else val[x]|=val[fail[x] ]; front++; for(i=0;i<2;i++){ if(next[x][i]==-1){ next[x][i]=next[fail[x] ][i]; } else{ fail[next[x][i] ]=next[fail[x] ][i]; rear++; q[rear]=next[x][i]; } } } } void debug(){ int i,j; for(j=0;j<=sz;j++){ printf("--->%d %d %d %d\n",j,val[j],next[j][0],next[j][1]); } } void bfs() { int i,j,state,x,t,tt,xx; memset(dist,0,sizeof(dist)); for(i=0;i<=n;i++){ for(j=0;j<=sz;j++)vis[j]=0; vis[jiedianhao[i] ]=1; if(i!=0)state=(1<<(i-1) ); else state=0; int front=1; int rear=0; rear++; q1[rear][0]=jiedianhao[i];q1[rear][1]=0; while(front<=rear){ x=q1[front][0];t=q1[front][1]; front++; for(j=0;j<2;j++){ xx=next[x][j]; tt=t+1; if(vis[xx])continue; if(val[xx]==-1)continue; vis[xx]=1; if(val[xx]!=0){ state|=val[xx]; dist[i][chuanhao[xx] ]=t+1; if(state==(1<<n)-1)break; } rear++; q1[rear][0]=xx;q1[rear][1]=tt; } if(state==(1<<n)-1)break; } } } void solve(){ int i,j,state,state1; for(i=0;i<=n;i++){ for(state=0;state<(1<<n);state++ ){ dp[i][state]=inf; } } dp[0][0]=0; for(state=0;state<(1<<n);state++){ //这个状压dp和tsp问题中的有些不同,这里是由已知的状态去推未知的状态,因为可能一个字符串包含其他字符串。 for(i=0;i<=n;i++){ if(dp[i][state]!=inf){ for(j=1;j<=n;j++){ dp[j][state|val[jiedianhao[j] ] ]=min(dp[j][state|val[jiedianhao[j] ] ],dp[i][state ]+dist[i][j] ); } } } } int minx=inf; for(i=0;i<=n;i++){ minx=min(minx,dp[i][(1<<n)-1]); } printf("%d\n",minx); } }ac; char s[1005],str[50050]; int main() { int i,j; while(scanf("%d%d",&n,&m)!=EOF) { if(n==0 && m==0)break; jiedianhao.clear(); chuanhao.clear(); ac.init(); for(i=1;i<=n;i++){ scanf("%s",s); ac.charu(s,i); } for(i=1;i<=m;i++){ scanf("%s",str); ac.charu(str,-1); } ac.build(); ac.bfs(); ac.solve(); } return 0; }