后缀自动机

clj大佬论文
自己对于后缀自动机的理解:每一个节点代表的是具有相同的right集合的子串(right集合的定义具体去看clj大佬的论文),整个最后构成的有向无环图,可以看成是一棵树,step[i]代表的是right[i]中子串的最长长度也就是论文中所说的Max(i),pre[i]代表的是i节点在树中的父节点,这里和AC自动机里的fail指针有点类似,往上找就相当于找到的是当前串的后缀在SAM中出现过的最长长度。
spoj 1811
题意:给两个串,求最长公共子串

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=1e6+10;
char s[maxn],s1[maxn];
int son[maxn][26],pre[maxn],step[maxn],last,total,root;//son儿子边是否存在 pre可以理解为parent树上的父节点  step到达i节点走的最长步数
inline void push_back(int v)
{
    step[++total]=v;
}
void Extend(char ch)
{
    push_back(step[last]+1);//新建ch的节点
    int p=last,np=total;
    for (;!son[p][ch]; p=pre[p]) son[p][ch]=np;//找到有ch出边的节点
    if (!p) pre[np]=root;//如果都没有则指向root
    else//按照论文里面的方法处理
    {
        int q=son[p][ch];
        if (step[q]!=step[p]+1)
        {
            push_back(step[p]+1);
            int nq=total;
            memcpy(son[nq],son[q],sizeof(son[q]));
            pre[nq]=pre[q];
            pre[q]=pre[np]=nq;
            for (; p!=-1&&son[p][ch]==q; p=pre[p]) son[p][ch]=nq;
        }
        else pre[np]=q;
    }
    last=np;
}
void Build()
{
    root=total=last=1;
    memset(son,0,sizeof(son));
    memset(pre,0,sizeof(pre));
    memset(step,0,sizeof(step));
    int len=strlen(s);
    for (int i=0; i<len; i++) Extend(s[i]-'a');
}
void debug()
{
    for(int i = 1; i <= total; ++i)
    {
        printf("id=%d, fa=%d, step=%d, ch=[ ", i, pre[i], step[i]);
        for(int j = 0; j < 26; ++j)
        {
            if(son[i][j])
                printf("%c,%d ", j+'a',son[i][j]);
        }
        puts("]");
    }
}
int main()
{
    while(~scanf("%s%s",s,s1))
    {
        Build();
        //debug();
        int ans=0;
        int p=root,len=strlen(s1),tmp=0;
        for(int i=0; i<len; i++)//用s1在后缀自动机上跑
        {
            if(son[p][s1[i]-'a'])//p节点有s1[i]的出边
                p=son[p][s1[i]-'a'],tmp++;
            else//没有就找出现过的最长后缀
            {
                while(p&&son[p][s1[i]-'a']==0)//找到最长的在s中出现过得后缀
                    p=pre[p];
                if(p)//有则从这个节点继续往下匹配
                {
                    tmp=step[p]+1;
                    p=son[p][s1[i]-'a'];
                }
                else//没有就从根重新开始
                    p=root,tmp=0;
            }
            ans=max(ans,tmp);//更新答案
        }
        printf("%d\n",ans);
    }
}

对于后缀自动机的理解
1.在parent树上某一节点的pre指向的节点代表的是可以和当前节点接收同样的后缀的状态节点。
2.parent树父节点的状态代表的子串是子节点的最长后缀。
3.sam中的转移可以直接对应所有后缀的开头。
4.出现次数向父亲传递,接收串数从儿子获取。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值