BZOJ_1195_[HNOI2006]最短母串_AC自动机+BFS+分层图
BZOJ_1195_[HNOI2006]最短母串_AC自动机+BFS+分层图
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自动机
然后因为n很小,每个节点维护fail到根的路径出现了哪些字符串(出现字符串的状态)
分1<<n个层,f[i][j]表示到第i个节点,当前已经有了的字符串状态为j,最短需要几个字符。
因为边权为1可以直接BFS一遍。
记录一下从哪来的方便输出路径。
代码:
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; #define N 650 int ch[N][26],fail[N],Q[N*4250],l,r,n,sta[N],cnt=1,flg[N],path[N][1<<12],zifu[N][1<<12]; char w[N]; void insert(int x) { int p=1,i; for(i=1;w[i];i++) { int &k=ch[p][w[i]-'A']; if(!k) k=++cnt; p=k; } sta[p]|=(1<<(x-1)); } void build_ac() { int i,p; for(i=0;i<26;i++) ch[0][i]=1; Q[r++]=1; while(l<r) { p=Q[l++]; for(i=0;i<26;i++) { if(ch[p][i]) fail[ch[p][i]]=ch[fail[p]][i],Q[r++]=ch[p][i]; else ch[p][i]=ch[fail[p]][i]; } sta[p]|=sta[fail[p]]; } } void print(int x,int y) { if(path[x][y]) { int d=path[x][y]; print(Q[d],Q[d+1]); } printf("%c",zifu[x][y]+'A'); } int main() { scanf("%d",&n); int mask=(1<<n)-1; int i; for(i=1;i<=n;i++) { scanf("%s",w+1); insert(i); } build_ac(); l=r=0; memset(path,-1,sizeof(path)); Q[r++]=1; Q[r++]=0; path[1][0]=0; while(l<r) { // puts("FUCK"); int x=Q[l++],y=Q[l++]; for(i=0;i<26;i++) { int c=ch[x][i]; if(path[c][y|sta[c]]==-1) { path[c][y|sta[c]]=l-2; zifu[c][y|sta[c]]=i; if((y|sta[c])==mask) { print(c,y|sta[c]); return 0; } Q[r++]=c; Q[r++]=y|sta[c]; } } } }