zoukankan      html  css  js  c++  java
  • 最短母串

    Ac_automaton的与状压的结合。

    看题解说是Ac_automaton上的dp,但是实际上没有十分明显的转移过程,仅仅使用状压的方式记录某个串是否被选择过了(当然有建完Ac_automation然后跑纯状压dp的解法)。

    首先建立Ac_automaton(Trie图),额外维护一个sta数组,该数组为一个二进制表示数组,‘1’表示该串出现过,‘0’表示该串未出现过。这个信息需要在insert与generate(build)过程中统计,然而有趣的是,网上的大部分题解仅有由fail更新当前节点的处理,而没有从父亲转移过来的信息,但依旧可以A掉。

    至于维护原理,由于fail指针指向最长后缀,试想,某串的后缀都出现了,他可能没出现吗?接着想,某串的父串(包含它的串,与子串相对)都出现了,那它可能没出现吗。

    接着是如何求解ans。首先思考,我们如何满足最短和字典序问题。

    bfs时,由于层层外推的特性,使得当我们遍历到一个可行状态,那么他就是最短的,因为我们此时说的“层”就是长度。

    字典序一般只要从A->Z枚举就可以保证(这个应该比上一个好想)。

    剩下的就是记录路径的问题了,我们可以在bfs结构体里维护一个id,表示当前状态推到下一个状态后,下一个状态父亲状态编号(好像说麻烦了),我们利用前继的方法来记录该状态是由那个状态而来,找到目标状态以后,直接递归输出即可。

    其实挺不好想得。

    具体实现细节可以参考代码,指针打完以后有点超时(后来发现我莫名其妙有一个map的logn),改成数组了,记录方便一点。

    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<cstdio>
    #include<vector>
    #include<queue>
    #include<set>
    #include<map>
    using namespace std;
    struct node{  
        int p,state,id;
    };
    struct Pre{  
        int nex,num;
    }pre[60000000];
    int trie[6000][26];
    int n,tot,root,cnt,lis[6000];
    int sta[6000],fail[6000];
    char s[60];
    bool v[6000][1<<15];
    void mclear(){  
        tot=root=1;    
    }
    void insert(int x){  
        int now=root,l=strlen(s+1);
        for(int i=1;i<=l;i++){  
            if(!trie[now][s[i]-'A']) trie[now][s[i]-'A']=++tot;
            now=trie[now][s[i]-'A'];    
        }sta[now]|=(1<<(x-1));
    }
    void generate(){  
        queue<int>q;
        for(int i=0;i<26;i++)
            if(trie[root][i]){  
                fail[trie[root][i]]=root;
                q.push(trie[root][i]);
            }else trie[root][i]=root;
        while(!q.empty()){  
            int now=q.front();
            q.pop();
            for(int i=0;i<26;i++)
                    if(trie[now][i]){  
                        fail[trie[now][i]]=trie[fail[now]][i];    
                        q.push(trie[now][i]);
                        sta[trie[now][i]]|=sta[now];
                    }else trie[now][i]=trie[fail[now]][i];
        }
        for(int i=1;i<=tot;i++)
            sta[i]|=sta[fail[i]];
    }
    void print(int x){  
        if(!x) return ;
        print(pre[x].nex);
        printf("%c",pre[x].num+'A');
    }
    void bfs(){  
        queue<node>q;int head=0,tail=1;
        q.push((node){1,0});
        v[1][0]=1;
        int ends=(1<<n)-1;
        while(!q.empty()){  
            node x=q.front();
            q.pop();
            int now=x.p,stt=x.state,id=x.id;
            if(ends==stt){  
                    print(id);
                    return ;    
            }
            for(int i=0;i<26;i++){  
                int y=trie[now][i];
                if(v[y][sta[y]|stt]) continue;
                pre[++cnt].nex=id;
                pre[cnt].num=i;
                v[y][sta[y]|stt]=1;
                q.push((node){y,sta[y]|stt,cnt});
            }    
        }
    }
    int main(){  
        scanf("%d",&n);
        mclear();
        for(int i=1;i<=n;i++){  
            scanf("%s",s+1);
            insert(i);
        }
        generate();
        bfs();
        return 0;
    }
    View Code

    交了小oj,结果这天bzoj好像维护,没试代码,缩进有点丑,凑合看看吧,主要明白为什么状压和bfs就好了,看明白后可以上网找一下状压dp的题解(虽然这个题主要是为了学Ac_automaton的)。

    小舟从此逝,沧海寄余生。

  • 相关阅读:
    第四章 利用函数实现指定的功能
    5-7 点到原点的距离(多态)
    5-2 宠物的生长(多态)
    5-7 学生cpp成绩统计
    5-6 学生CPP成绩计算
    php将远程图片下载保存到本地
    vs2010 调试快捷键
    vs2010 快捷键大全
    [C#] 使用Application.AddMessageFilter当做Form的热键
    C# 收发和处理自定义的WINDOWS消息
  • 原文地址:https://www.cnblogs.com/Yu-shi/p/11072109.html
Copyright © 2011-2022 走看看