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

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];
            }
        }
    }
}

  

posted @ 2018-06-15 13:39  fcwww  阅读(357)  评论(0编辑  收藏  举报