一道纠结了好久的题,主要是因为一开始思路就想错了,还按着错误的思路想了好久。。。
题目大意就是要找一个最短的字符串,包含所有的合法单词并且不包含所有的非法单词。
题目中给的非法单词非常多,但是合法单词很少,很容易想到用状态压缩来做。我一开始想到的是把每个合法单词当作一个点,然后用哈密顿回路来做,想了半天才发现这个思路明显是错误的。。。。。。
正确的做法是把所有可以作为合法单词结尾的点选出来,BFS出每两点之间的距离,每个点都会有一个压缩状态标记这个点可以作为哪几个单词的结尾,然后DP就可以了。
d[state][v]表示最后一步在v时到达state这个状态需要的最小长度,方程为d[stat|flag[pnt[v]]][v]=min(d[stat|flag[pnt[v]]][v],d[stat][u]+map[u][v]),其中flag[pnt[v]]表示以v这个点的状态。
#include <stdio.h> #include <string.h> #define INF 0x3f3f3f3f #define MAXN 60005 char s[1002]; int n,m; int next[MAXN][2],fail[MAXN],flag[MAXN],pos; int newnode(){ next[pos][0]=next[pos][1]=0; fail[pos]=flag[pos]=0; return pos++; } void insert(char *s,int id){ int p=0; for(int i=0;s[i];i++){ int k=s[i]-'0',&x=next[p][k]; p=x?x:x=newnode(); } if(id==-1)flag[p]=-1; else flag[p]|=(1<<id); } int q[MAXN],front,rear; void makenext(){ q[front=rear=0]=0,rear++; while(front<rear){ int u=q[front++]; for(int i=0;i<2;i++){ int v=next[u][i]; if(v==0)next[u][i]=next[fail[u]][i]; else q[rear++]=v; if(v&&u){ fail[v]=next[fail[u]][i]; if(flag[fail[v]]==-1)flag[v]=-1; else flag[v]|=flag[fail[v]]; } } } } #define MAXM 50 int d[1025][MAXM],pnt[MAXM],ps,map[MAXM][MAXM],dis[MAXN]; inline int min(int x,int y){return x<y?x:y;} void bfs(int p){ for(int i=0;i<pos;i++)dis[i]=INF; q[front=rear=0]=pnt[p],rear++; dis[pnt[p]]=0; while(front<rear){ int u=q[front++]; for(int i=0;i<2;i++){ int v=next[u][i]; if(flag[v]<0||dis[v]!=INF)continue; dis[v]=dis[u]+1; q[rear++]=v; } } for(int i=0;i<ps;i++)map[p][i]=dis[pnt[i]]; } int dp(){ for(int i=0;i<(1<<n);i++)for(int u=0;u<ps;u++)d[i][u]=INF; d[0][0]=0; for(int i=0;i<(1<<n);i++){ for(int u=0;u<ps;u++){ if(d[i][u]==INF)continue; for(int v=0;v<ps;v++) d[i|flag[pnt[v]]][v]=min(d[i|flag[pnt[v]]][v],d[i][u]+map[u][v]); } } int ans=INF; for(int i=0;i<ps;i++) ans=min(ans,d[(1<<n)-1][i]); return ans; } int main(){ // freopen("test.in","r",stdin); while(scanf("%d%d",&n,&m),n||m){ pos=0;newnode(); for(int i=0;i<n;i++){ scanf("%s",s); insert(s,i); } for(int i=0;i<m;i++){ scanf("%s",s); insert(s,-1); } makenext(); ps=0; for(int i=0;i<pos;i++) if(i==0||flag[i]>0)pnt[ps++]=i; for(int i=0;i<ps;i++) bfs(i); printf("%d\n",dp()); } return 0; }