BZOJ1195最短母串——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自动机上建出所有读入的串,并且我们可以用一个二进制数来记录每个串是否被选取的状态。由于要求的是最短的,所以我们进行BFS,第一个完成二进制所有位上都是以的状态就是题目要求的状态,又由于要字典序最小,所以BFS时从A到Z枚举即可。
#include<bits/stdc++.h>
#define MAXN 605
using namespace std;
int read(){
    char c;int x;while(c=getchar(),c<'0'||c>'9');x=c-'0';
    while(c=getchar(),c>='0'&&c<='9') x=x*10+c-'0';return x;
}
int n,cnt=1,nxt[MAXN][26],p[MAXN],fail[MAXN],q[MAXN],ans[MAXN],vis[MAXN][4100];
struct node{
    int p,status;
};
struct stevensonson{
    int lst,num;
}L[MAXN*4100];
queue<node> a;
char s[52];
void build(int x){
    int l=strlen(s+1),pl=1;
    for(int i=1;i<=l;i++){
        int x=s[i]-'A';
        if(nxt[pl][x]) pl=nxt[pl][x];
        else{
            ++cnt;nxt[pl][x]=cnt;pl=cnt;
        }
    }
    p[cnt]|=(1<<x-1);
}
void getfail(){
    int h=0,t=0;
    for(int i=0;i<26;i++)
      if(nxt[1][i]) fail[q[++t]=nxt[1][i]]=1;
      else nxt[1][i]=1;
    while(h<t){
        int now=q[++h];
        for(int i=0;i<26;i++){
            if(nxt[now][i]){
                p[nxt[now][i]]|=p[nxt[fail[now]][i]]; 
                fail[q[++t]=nxt[now][i]]=nxt[fail[now]][i];
                if(!fail[nxt[now][i]]) fail[nxt[now][i]]=1;
            }
            else nxt[now][i]=nxt[fail[now]][i];
        }
    }
}
void bfs(){
    int h=0,t=1,k=0,end=(1<<n)-1;a.push((node){1,0});
    while(h<t){
        node e=a.front();a.pop();h++;
        if(e.status==end){
            for(int i=h;i>1;i=L[i].lst) ans[++k]=L[i].num;
            for(int i=k;i;i--) putchar('A'+ans[i]);
            return;
        }
        for(int i=0;i<26;i++){
            int to=nxt[e.p][i];
            if(vis[to][e.status|p[to]]) continue;
            vis[to][e.status|p[to]]=1;L[++t].lst=h;L[t].num=i;
            a.push((node){to,e.status|p[to]});
        }
    }
}
int main()
{
    n=read();
    for(int i=1;i<=n;i++){
        scanf("%s",s+1);
        build(i);
    }
    getfail();
    bfs();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值