最短母串

Posted yu-shi

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了最短母串相关的知识,希望对你有一定的参考价值。

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;

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

以上是关于最短母串的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ 1195: [HNOI2006]最短母串

bzoj1195 最短母串

P2322 [HNOI2006]最短母串问题(AC自动机)

最短母串

题解最短母串

bzoj1195 [HNOI2006]最短母串