后缀自动机

时间跨度2 weeks,我到现在才刚刚看懂一个构造,果然是过于菜了……

时间原因,写得非常潦草而缺少细节,题也还没刷,所以建议点开大佬的链接进行学习qwq

啊就各种鹤(%%%KesdiaelKen tql

用途

把一个字符串中的所有子串压缩起来,用endpos和len来描述,既省空间又能保留位置信息(空间和时间都是线性的!),一般用途(用其他方法也可以):判断某一个串是否为原串的子串(从原点跑这个串,跑到NULL说明不是子串),不同字串个数(DAG上DP)……,特殊用途(和在原串中出现位置有关的一些信息):出现的次数,第一次出现的位置,出现在某个位置之后的次数……

重要的引理

这个……去OIWiki上看吧(逃,可能我以后会完善一下?(多鹤一鹤)

还有parent tree,我也略了……感觉最不容易理解的是怎么把parent tree和所有子串扯上关系把qwq

构造方法

构造的原则是过程中保证每一个点都代表一个endpos等价类,不需要考虑集合里面有啥,保证同一个点一定一样,不同的点一定不一样就够了。

跳的fa是parent tree的连边(不是后缀自动机的边),用的节点和parent tree完全相同,但是边就很不一样了。字母在边上,从源点出发到达点i的任意一条路径形成的字符串均属于点i代表的类(属于:这个节点的类包含此子串,不要理解成此子串是这个节点的类可以表示的最长串的子串)。

从上一个终止位置往上跳,可以按照长度从大到小的顺序找到还没有加入这个新字母时的串的所有后缀,endpos可能发生变化的只有新串的后缀(旧串的后缀+c),只需要处理好这些endpos发生变化的字符串,那就遍历旧串的后缀自动机的终止节点(找到所有新串的后缀),判断它们的endpos是否变化。

对于任意一个前缀,它在所属的类中长度最长,相邻两个前缀所属点之间肯定有连边(所以把前缀写到一条线上会很整齐)。

将所有的 是旧串后缀且在其后面+c形成的新字符串不是旧串子串 的后缀 往新串的最大后缀所属节点连一条边。因为这些新字符串的endpos只有n,和前缀np相同。

有ch[c]的终止理由:+c的字符串已经在旧串里出现过了,endpos不只有n,那么不同。(当然还要保证p>=1)

第一种情况就是ch[c]永远都不会有,除了1,没有节点的endpos包含n,fa[n]就是1了。

如果找到了ch[c],长度差1的情况说明指向ch[c]之后的节点全体都是当前字符串(新串)的后缀,endpos一致,那么找到的ch[c]是最长的,fa[np]为ch[c],终止理由:1是前面的不够长,不可以更新fa[np],2是前面的相当于是子集,是更小的后缀,endpos更不会变,没有check的必要。

长度差不是1,那么一定大于1(指向关系决定的),假设到达q,属于q的长度不大于len(p)+1的串是新串的后缀,但>len(p)+1的串不是,那么把q拆成不相交的nq和q,nq+q=q,nq的长度是len(p)+1,nq比q多了一个n其余的相同,fa(q)=nq。终止理由:q的父亲是nq,endpos包含n,q的祖先的endpos都包含n,再往上跳都不会出现一个节点两种endpos的情况了。

以后有空了再好好写吧……我还是先做点题去(逃*2

code

//叩首问路,码梦为生
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 1e5 + 3;

struct node 
{
    int ch[26];
    int len, fa;
    node() {memset(ch, 0, sizeof(ch)); len = 0;}
}dian[maxn<<1];
int las = 1, tot = 1;
void add(int c)
{
    int p = las; int np = las = ++tot;
    dian[np].len = dian[p].len + 1;
    for(; p&&!dian[p].ch[c]; p=dian[p].fa) dian[p].ch[c] = np;
    if(!p) dian[np].fa = 1;
    else 
    {
        int q = dian[p].ch[c];
        if(dian[q].len == dian[p].len + 1) dian[np].fa = q;
        else 
        {
            int nq = ++tot; dian[nq] = dian[q];
            dian[nq].len = dian[p].len + 1;
            dian[q].fa = dian[np].fa = nq;
            for(; p&&dian[p].ch[c]==q; p=dian[p].fa) dian[p].ch[c] = nq;
        }
    }
}
char s[maxn];
int len;

int main()
{
    scanf("%s", s); len = strlen(s);
    for(int i=0; i<len; i++) add(s[i]-'a');

    return 0;
}

 

posted @ 2022-12-31 23:00  Catherine_leah  阅读(67)  评论(1编辑  收藏  举报
/* */