1195: [HNOI2006]最短母串
Time Limit: 10 Sec Memory Limit: 32 MBSubmit: 2037 Solved: 689
Description
给定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自动机,标记好每个结点对应的包含串状态,然后从根节点开始BFS(BFS保证串最短),在每个状态从A到Z依次尝试扩展状态(保证字典序最小)
代码
#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<queue>
using namespace std;
const int N=605;
int L1[N*(1<<12)],L2[N*(1<<12)],n,ans[N],num=0,ch[N][26],p[N],end[N],S,tot,q[670],head,tail;
queue<int>q1,q2;
bool vis[N][(1<<12)];
char s[60];
void init() {S=tot=1;memset(end,0,sizeof end);return;}
void insert(char *s,int id){
int len=strlen(s),now=S;
for(int i=0;i<len;i++){
if(!ch[now][s[i]-'A']) ch[now][s[i]-'A']=++tot;
now=ch[now][s[i]-'A'];
}
end[now]|=(1<<id);
}
void pre(){
head=1; tail=0;
for(int i=0; i<26; i++)
if(ch[S][i]){q[++tail]=ch[S][i];p[ch[S][i]]=S;} else ch[S][i]=S;
while(head<=tail){
int u=q[head++];
int v,r;
for(int i=0; i<26; i++){
if(ch[u][i]){
p[ch[u][i]]=ch[p[u]][i];
end[ch[u][i]]|=end[ch[p[u]][i]];
q[++tail]=ch[u][i];
}
else ch[u][i]=ch[p[u]][i];
}
}
return;
}
void solve(){
head=1; tail=1;int ed=(1<<n)-1;
q1.push(S),q2.push(0);
while(head<=tail){
int u=q1.front();q1.pop();
int e=q2.front();q2.pop();
if(e==ed){
for(;head>1; head=L2[head]){ans[++num]=L1[head];}
for(int i=num; i; i--) printf("%c",ans[i]+'A');
return;
}
for(int i=0; i<26; i++){
if(!vis[ch[u][i]][e|end[ch[u][i]]]){
L1[++tail]=i;
L2[tail]=head;
q1.push(ch[u][i]);
q2.push(e|end[ch[u][i]]);
vis[ch[u][i]][e|end[ch[u][i]]]=1;
}
}
head++;
}
}
int main(){
scanf("%d",&n);
init();
for(int i=0; i<n; i++)scanf("%s",s),insert(s,i);
pre();
solve();
return 0;
}
miao~~~