广义后缀自动机模板

后缀自动机能解决很多单串的问题、但是一旦到了多串的情况、可能就会变得有些棘手

这个时候你可能会想能不能将多个串一起构建出和单串后缀自动机那样子拥有诸多优美性质的自动机呢?

答案当然是有的、那就是广义后缀自动机

对于广义后缀自动机、和普通的后缀自动机写法上有些许不同之处

大致就是在插入新串的时候、需要把当前状态指针 last 指回 root

还有一个问题、网上的人们都说广义后缀自动机在新插入节点的时候要判是否已经存在

这个就造成了代码的迥异

关于代码、借鉴了这个博客 ==> Click here

下面仅仅给出模板

struct General_Suffix_Automaton{
    static const int maxn = ((int)1e5 + 10) * 2;
    static const int Letter = 26;
    int fa[maxn];
    int len[maxn];
    int ch[maxn][Letter];
    int cnt[maxn];
    int tpSum[maxn];
    int tp[maxn];
    int tot;
    int last;
    int mxLen;

    inline void init(){
        last = tot = 1;
        len[1] = 0;
        mxLen = 0;
        memset(ch[1], 0, sizeof(ch[1]));
        memset(cnt, 0, sizeof(cnt));
    }

    inline void add(int x){
        int p = last;
        if(ch[p][x] && len[ch[p][x]] == len[p] + 1){
            last = ch[p][x];
            return ;
        }///就是在这里特判、下面的代码就基本和普通的SAM差不多了
        int np = ++tot;
        memset(ch[np], 0, sizeof(ch[np]));
        last = np;
        len[np] = len[p] + 1;
        while(p && !ch[p][x]) ch[p][x] = np, p = fa[p];
        if(!p) fa[np] = 1;
        else{
            int q = ch[p][x];
            if(len[q] == len[p] + 1) fa[np] = q;
            else{
                int nq = ++tot;
                len[nq] = len[p] + 1;
                memcpy(ch[nq], ch[q], sizeof(ch[q]));
                fa[nq] = fa[q];
                fa[q] = fa[np] = nq;
                while(p && ch[p][x] == q) ch[p][x] = nq, p = fa[p];
            }
        }
    }

    inline void addStr(char * s){
        last = 1;
        int len = strlen(s);
        mxLen = max(mxLen, len);
        for(int i=0; i<len; i++) add(s[i] - ‘a‘),cnt[last]++;
    }

    int deg[maxn];
    inline void toposort(){
        for(int i = 1; i <= mxLen; i++)tpSum[i] = 0;
        for(int i = 1; i <= tot; i++)tpSum[len[i]]++;
        for(int i = 1; i <= mxLen; i++)tpSum[i] += tpSum[i-1];
        for(int i = 1; i <= tot; i++)tp[tpSum[len[i]]--] = i;
        for(int i = tot; i; i--)cnt[fa[tp[i]]] += cnt[tp[i]];
//        用下面的队列来构造拓扑序也是可以的
//        queue<int> que;
//        while(!que.empty()) que.pop();
//        int tmp = tot;
//        for(int i=1; i<=tot; i++) deg[fa[i]]++;
//        for(int i=1; i<=tot; i++) if(!deg[i]) que.push(i);
//        while(!que.empty()){
//            int x = que.front();
//            que.pop();
//            tp[tmp--] = x;
//            deg[fa[x]]--;
//            if(!deg[fa[x]]) que.push(fa[x]);
//        }
//        for(int i=tot; i>=1; i--) cnt[fa[tp[i]]] += cnt[tp[i]];
    }

    inline void GetAns(char *s){
        ///....
    }
}sam;

原文地址:https://www.cnblogs.com/LiHior/p/9940403.html

时间: 2024-08-29 20:55:14

广义后缀自动机模板的相关文章

【模板】广义后缀自动机模板

建立在tire上的后缀自动机,采用bfs的建图方式,可以保证复杂度 #include<bits/stdc++.h> using namespace std; #define fu(a,b,c) for(int a=b;a<=c;++a) #define fd(a,b,c) for(int a=b;a>=c;--a) #define MAXN 2000010 #define LL long long const int CHARSET_SIZE=10; //Suffix_Automa

【BZOJ3926】[Zjoi2015]诸神眷顾的幻想乡 广义后缀自动机

[BZOJ3926][Zjoi2015]诸神眷顾的幻想乡 Description 幽香是全幻想乡里最受人欢迎的萌妹子,这天,是幽香的2600岁生日,无数幽香的粉丝到了幽香家门前的太阳花田上来为幽香庆祝生日. 粉丝们非常热情,自发组织表演了一系列节目给幽香看.幽香当然也非常高兴啦. 这时幽香发现了一件非常有趣的事情,太阳花田有n块空地.在过去,幽香为了方便,在这n块空地之间修建了n-1条边将它们连通起来.也就是说,这n块空地形成了一个树的结构. 有n个粉丝们来到了太阳花田上.为了表达对幽香生日的祝

hdu4622(后缀自动机模板)

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=4622 题意: 先输入一个长度为 n 的字符串, 然后有 q 个形如 l, r 的询问, 对于每个询问输出区间 [l, r] 中有多少不同的子串 思路: 后缀自动机模板 代码: 1 // 后缀自动机要理解其含义,从起点到每个点的不同路径,就是不同的子串. 2 // 到每一个点,不同路径,其实就是以这个点为最后一个字符的后缀,长度是介于(p->fa->len,p->len]之间的,个数也就清楚

E. Three strings 广义后缀自动机

http://codeforces.com/problemset/problem/452/E 多个主串的模型. 建立一个广义后缀自动机,可以dp出每个状态的endpos集合大小.同时也维护一个R[]表示那个串出现过. 所以可以算出每个状态的dp[i][k]表示第k个串在第i个状态中出现的次数. 可以知道sigma dp[i][0...k]是等于  endpos集合的大小. 然后把这个贡献加到min(i)....max(i)中去就可以了 差分一下. #include <bits/stdc++.h>

BZOJ 3277 串 (广义后缀自动机)

3277: 串 Time Limit: 10 Sec Memory Limit: 128 MB Submit: 309 Solved: 118 [Submit][Status][Discuss] Description 字符串是oi界常考的问题.现在给定你n个字符串,询问每个字符串有多少子串(不包括空串)是所有n个字符串中至少k个字符串的子串(注意包括本身). Input 第一行两个整数n,k. 接下来n行每行一个字符串. Output 输出一行n个整数,第i个整数表示第i个字符串的答案. Sa

广义后缀自动机

1).自动机的介绍 首先我们先来介绍一下什么是自动机,有限状态自动机的功能是识别字符串,令一个自动机A,若他能识别字符串S,就记为A(S)=Ture,否则A(S)=False. 自动机由五个部分组成,alpha:字符集,state:状态集合,init:初始状态,end:结束状态集合,trans:状态转移函数. 令trans(s,ch)表示当前状态是s,在读入字符ch之后,所到达的状态.如果trans(s,ch)这个转移不存在,为了方便,设其为null,同时null只能转移到null.null表示

hdu 5853 Jong Hyok and String(广义后缀自动机)

题目链接:hdu 5853 Jong Hyok and String 题意: 给你n个字符串,m个询问,每次询问一个字符串 定义set(s)={(i,j)} 表示 s在第i个字符串中出现,且末尾位置为j. 对于一个询问,求set(Qi)=set(t) ,t串的数量. 题解: 如果是n=1,那么就是后缀自动机的一道裸题,答案就是Qi串匹配的最后一个节点x,ml[x]-ml[f[x]]. 现在是多个串,那么就建立一个广义后缀自动机.每次插入一个串后,将last=root,然后继续插下一个就行了. 最

【BZOJ2806】【CTSC2012】Cheat 广义后缀自动机+二分+Dp

题目 题目在这里 思路&做法 我们先对标准作文库建广义后缀自动机. 然后对于每一篇阿米巴的作文, 我们首先把放到广义后缀自动机跑一遍, 对于每一个位置, 记录公共子串的长度\((\)即代码和下文中的\(val\)数组\()\) 接着我们二分答案, 用DP检验. Dp方程很好想, \(d_i = max \{ d_j + i - j \ | \ i-val_i <= j <= i-lim \}\) 可以用单点队列优化. 代码 #include <iostream> #incl

【codeforces666E】Forensic Examination 广义后缀自动机+树上倍增+线段树合并

题目描述 给出 $S$ 串和 $m$ 个 $T_i$ 串,$q$ 次询问,每次询问给出 $l$ .$r$ .$x$ .$y$ ,求 $S_{x...y}$ 在 $T_l,T_{l+1},...,T_r$ 中的哪一个里出现次数最多,输出出现次数最多的串编号(如果有多个则输出编号最小的)以及相应出现次数. $|S|,q\le 5\times 10^5$ ,$\sum\limits_{i=1}^m|T_i|\le 5\times 10^4$ . 题解 广义后缀自动机+树上倍增+线段树合并 对 $S$