[BZOJ1195]最短母串
1195: [HNOI2006]最短母串
Time Limit: 10 Sec Memory Limit: 32 MBDescription
给定n个字符串(S1,S2,„,Sn),要求找到一个最短的字符串T,使得这n个字符串(S1,S2,„,Sn)都是T的子串。
Input
第一行是一个正整数n(n<=12),表示给定的字符串的个数。以下的n行,每行有一个全由大写字母组成的字符串。每个字符串的长度不超过50.
Output
只有一行,为找到的最短的字符串T。在保证最短的前提下,如果有多个字符串都满足要求,那么必须输出按字典序排列的第一个。
Sample Input
2
ABCD
BCDABC
ABCD
BCDABC
Sample Output
ABCDABC
题解:
一开始的确想到了状压……但是没有想到后面的操作……
而且这个题让我不得不打数组版……痛心疾首。
如果我们按照AC自动机来做这道题,我们可以这样考虑:
建好trie图之后,从根节点跑一个BFS最短路,并且按照A~Z的顺序往下走,
那么我们就实现了“最短”和“字典序最小”。
而对于字符串,我们可以用状压来记录每个串是否出现。
代码见下:
1 #include<cstdio> 2 #include<cstring> 3 using namespace std; 4 const int K=1<<12; 5 const int N=610; 6 short n,cnt,bit[20]; 7 char s[15][60],ans[N],c[N*K]; 8 short val[N],ch[N][26],f[N]; 9 bool vis[N][K]; 10 int from[N*K],q[N],hd,tl; 11 short state[N*K],point[N*K]; 12 inline void get_fail() 13 { 14 hd=0,tl=0; 15 for(int i=0;i<26;i++) 16 if(ch[0][i])q[tl++]=ch[0][i]; 17 while(hd^tl) 18 { 19 int rt=q[hd++]; 20 for(int i=0;i<26;i++) 21 { 22 int u=f[rt]; 23 if(ch[rt][i]) 24 { 25 q[tl++]=ch[rt][i]; 26 while(u&&!ch[u][i])u=f[u]; 27 f[ch[rt][i]]=ch[u][i]; 28 val[ch[rt][i]]|=val[ch[u][i]]; 29 } 30 else 31 ch[rt][i]=ch[u][i]; 32 } 33 } 34 } 35 inline void get_ans() 36 { 37 hd=0,tl=1; 38 while(hd^tl) 39 { 40 int u=point[hd],st=state[hd]; 41 if(st==bit[n]-1) 42 { 43 int id=0; 44 while(hd)ans[++id]=c[hd],hd=from[hd]; 45 while(id>=1)putchar(ans[id]),id--; 46 return; 47 } 48 for(int i=0;i<26;i++) 49 { 50 if(!vis[ch[u][i]][st|val[ch[u][i]]]) 51 { 52 point[tl]=ch[u][i]; 53 c[tl]=i+'A',from[tl]=hd; 54 state[tl]=st|val[ch[u][i]]; 55 vis[point[tl]][state[tl]]=1; 56 tl++; 57 } 58 } 59 hd++; 60 } 61 } 62 int main() 63 { 64 scanf("%d",&n); 65 bit[0]=1;for(int i=1;i<=n;i++)bit[i]=bit[i-1]<<1; 66 for(int i=1;i<=n;i++) 67 { 68 scanf("%s",s[i]); 69 int rt=0;int m=strlen(s[i]); 70 for(int j=0;j<m;j++) 71 { 72 int d=s[i][j]-'A'; 73 if(!ch[rt][d])ch[rt][d]=++cnt; 74 rt=ch[rt][d]; 75 } 76 val[rt]|=bit[i-1]; 77 } 78 get_fail();get_ans(); 79 }
Progress is not created by contented people.