zoukankan      html  css  js  c++  java
  • P2322 [HNOI2006]最短母串问题

    P2322 [HNOI2006]最短母串问题

    AC自动机+bfs

    题目要求:在AC自动机建的Trie图上找到一条最短链,包含所有带结尾标记的点

    因为n<12,所以我们可以用二进制保存状态:某个带结尾标记的点是否被处理到。

    把编号为 i 的结尾标记设为2^(i-1)

    然后跑一遍bfs,如果跑到某个点结尾标记之和=2^n-1,那么就说明答案找到了

    开2个数组保存每个bfs状态的对应的上一个编号和对应字母

    输出的时候递归即可

    attention:保存bfs状态的数组一定要开的很大(2e5/1e6/2e6 70pts/90pts/AC)

    对于字典序问题:查找顺序都是'A'->‘Z’,不用担心(我还瞎操心个啥qaq)

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    using namespace std;
    struct data{
        int nxt[26],fail,last,end;
    }a[602];
    struct node{int id,t,state;};
    char q[55];
    int n,cnt1,cnt2,pre1[2000001],pre2[2000001];
    bool vis[4097][602];
    inline void Trie_build(int id){
        scanf("%s",q);
        int u=0,len=strlen(q);
        for(int i=0;i<len;++i){
            int p=q[i]-'A';
            if(!a[u].nxt[p]) a[u].nxt[p]=++cnt1;
            u=a[u].nxt[p];
        }a[u].end|=1<<(id-1); //标记值=2^(i-1)
    }
    void AC_build(){
        queue <int> h;
        for(int i=0;i<26;++i) if(a[0].nxt[i]) h.push(a[0].nxt[i]);
        while(!h.empty()){
            int x=h.front(); h.pop();
            for(int i=0;i<26;++i){
                int &to=a[x].nxt[i];
                if(to){
                    a[to].fail=a[a[x].fail].nxt[i];
                    a[to].last= a[a[to].fail].end ? a[to].fail:a[a[to].fail].last;
                    a[to].end|=a[a[to].last].end; //累加上所有后缀的状态
                    h.push(to);
                }else to=a[a[x].fail].nxt[i];
            }
        }
    }
    inline void print(int x){ //递归输出
        if(!x) return;
        print(pre1[x]);
        putchar(pre2[x]+'A');
    }
    void bfs(){
        queue <node> h;
        h.push((node){0,0,a[0].end});
        while(!h.empty()){
            node x=h.front(); h.pop();
            if(x.state==(1<<n)-1) {print(x.t); return ;}
            for(int i=0;i<26;++i){ //字典序从小到大
                node to=(node){a[x.id].nxt[i],233,x.state|a[a[x.id].nxt[i]].end}; //状态更新
                if(vis[to.state][to.id]) continue; //去重
                vis[to.state][to.id]=1; to.t=++cnt2; //给一个新编号
                pre1[cnt2]=x.t; pre2[cnt2]=i; //用两个数组存该编号对应的字母和上级的编号
                h.push(to);
            }
        }
    }
    int main(){
        scanf("%d",&n);
        for(int i=1;i<=n;++i) Trie_build(i);
        AC_build(); bfs();
        return 0;
    }
  • 相关阅读:
    ES6入门 阮一峰
    NPM
    移动端BUG
    配置每次git push 不需要输入账号密码
    移动端rem布局,用户调整手机字体大小或浏览器字体大小后导致页面布局出错问题
    课程表
    岛屿数量
    二叉树的右视图
    c++设计模式——工厂模式
    克隆图
  • 原文地址:https://www.cnblogs.com/kafuuchino/p/9625851.html
Copyright © 2011-2022 走看看