1. 程式人生 > >BZOJ 1195 [HNOI2006]最短母串 (Trie圖+狀壓+bfs最短路)

BZOJ 1195 [HNOI2006]最短母串 (Trie圖+狀壓+bfs最短路)

BZOJ1195 LOJ10061

題目大意:給你$n$個模式串,求一個最短且字典序最小的文字串並輸出這個串,$n<=12,len<=50$

首先對所有模式串構造$Trie$圖,$Trie$圖的性質和$DP$的性質簡直是完美契合..

模式串數量很少,考慮狀壓

定義$f[x][s]$表示現在所在$Trie$圖內的位置為$x$,已經匹配到的串的狀態為$s$,此時需要文字串的最短長度

轉移十分顯然,$f[fail_{x}][s|ed[fail_{x}])]=min(f[x][s])+1$

最後找出最小的$f[x][(1<<n)-1]$

然而...直接這樣轉移我們沒辦法統計答案啊

考慮把問題放到圖上,利用$bfs$佇列先進先出的性質,如果我們優先把字典序小的狀態推進佇列,再通過記錄當前狀態第一次被更新時的上一層狀態$fa[..]$的方式記錄路徑,跑$bfs$最短路,當佇列中出現$s==(1<<n)-1$時立即結束,此時我們求得的一定是長度最短且字典序最小的答案!

注意可能有相同的串

注意不要忘了建$fail$樹,如果某個節點是某個串的結尾,也要把它是這個串的結尾的資訊傳遞到它在$fail$樹中的子樹中去!我沒傳竟然還有70分

  1 #include <queue>
  2 #include <cstdio>
  3
#include <cstring> 4 #include <algorithm> 5 #define NN 610 6 #define MM 4100 7 #define ll long long 8 #define uint unsigned int 9 #define ull unsigned long long 10 #define inf 0x3f3f3f3f 11 #define idx(x) (x-'A') 12 using namespace std; 13 14 int n,cte; 15 int
head[NN]; 16 char str[NN]; 17 struct Edge{int to,nxt;}edge[NN*2]; 18 void ae(int u,int v){ 19 cte++;edge[cte].nxt=head[u]; 20 edge[cte].to=v,head[u]=cte; 21 } 22 struct node{ 23 int x,s; 24 node(int x,int s):x(x),s(s){} 25 node(){} 26 }fa[NN][MM]; 27 int dis[NN][MM]; 28 namespace AC{ 29 int ch[NN][26],fail[NN],ed[NN],val[NN],tot; 30 void Build_Trie(char *str,int len,int id) 31 { 32 int x=0; 33 for(int i=1;i<=len;i++){ 34 if(!ch[x][idx(str[i])]) 35 ch[x][idx(str[i])]=++tot,val[tot]=idx(str[i]); 36 x=ch[x][idx(str[i])]; 37 }ed[x]|=(1<<(id-1)); 38 } 39 void Build_Fail() 40 { 41 queue<int>q; 42 for(int i=0;i<26;i++) 43 if(ch[0][i]) q.push(ch[0][i]); 44 while(!q.empty()) 45 { 46 int x=q.front();q.pop(); 47 ae(fail[x],x); 48 for(int i=0;i<26;i++) 49 { 50 if(ch[x][i]){ 51 fail[ch[x][i]]=ch[fail[x]][i]; 52 q.push(ch[x][i]); 53 }else{ 54 ch[x][i]=ch[fail[x]][i]; 55 } 56 } 57 } 58 q.push(0); 59 while(!q.empty()) 60 { 61 int x=q.front();q.pop(); 62 for(int j=head[x];j;j=edge[j].nxt){ 63 int v=edge[j].to; 64 ed[v]|=ed[x]; 65 q.push(v); 66 } 67 } 68 } 69 node Bfs() 70 { 71 queue<node>q; 72 memset(dis,0x3f,sizeof(dis)); 73 q.push(node(0,0)); 74 dis[0][0]=0; 75 node k1,k2; 76 int x,v,s,t; 77 while(!q.empty()) 78 { 79 k1=q.front();q.pop(); 80 x=k1.x,s=k1.s; 81 if(s==(1<<n)-1) return k1; 82 for(int i=0;i<26;i++) 83 { 84 v=ch[x][i],t=s|ed[v]; 85 if(dis[v][t]>dis[x][s]+1){ 86 dis[v][t]=dis[x][s]+1; 87 fa[v][t]=k1; 88 q.push(node(v,t)); 89 } 90 } 91 } 92 /*for(int i=1;i<=tot;i++) 93 printf("%d:%d\n",i,dis[i][(1<<n)-1]);*/ 94 } 95 char Ans[NN]; 96 void solve() 97 { 98 for(int i=1;i<=n;i++){ 99 scanf("%s",str+1); 100 int len=strlen(str+1); 101 Build_Trie(str,len,i);} 102 Build_Fail(); 103 node ans=Bfs(); 104 node a=ans; 105 int num=0; 106 while(a.x||a.s){ 107 Ans[++num]=val[a.x]+'A'; 108 a=fa[a.x][a.s];} 109 for(int i=num;i>0;i--) 110 printf("%c",Ans[i]); 111 puts(""); 112 } 113 }; 114 115 int main() 116 { 117 //freopen("t2.in","r",stdin); 118 scanf("%d",&n); 119 AC::solve(); 120 return 0; 121 }