[模板] 后缀自动机

简介

后缀自动机是一种确定性有限状态自动机, 它可以接收字符串\(s\)的所有后缀.

构造, 性质

翻译自毛子俄罗斯神仙的博客, 讲的很好
后缀自动机详解 - DZYO的博客 - CSDN博客

定义

  • 对于字符串\(s\)的子串\(t\), \(endpos(t)\) (或者 \(right(t)\) ) 表示t在s中出现位置的右短点的集合.
    • \(endpos\)互不相交.
  • 后缀自动机的节点 \(p\) 代表一个 \(endpos\) 相同的子串的集合.
  • 对于后缀自动机的节点 \(p\), \(parent(p)\) 表示p在不同等价类中的最长后缀.
    • \(parent\) 形成一棵树关系.

endpos集合大小

  • 对于不是拷贝的节点, cnt设为1; 拷贝而来的节点, cnt设为0.
  • 在parent树上dp, \(cnt_p+=\sum_{parent(v)=p} cnt_v\).
  • \(cnt_p\) 表示这个节点endpos集合大小, 也就是在字符串中的出现次数.

正确性: 一个子串一定是原串某个前缀的后缀.
不是拷贝的节点代表一个前缀; 它的parent代表它的一个在不同等价类中的后缀.
对于一个前缀的后缀, 它的 \(\sum_{parent(v)=p} cnt_v\) 即为它做为其他前缀的后缀的出现次数.

Code

const int nsz=1e6+50,ndsz=2*nsz,csz=27;

ll n;
char s[nsz];

//sam
struct tnd{int ch[csz],l,fa,cnt;}sam[ndsz];
#define ch(p,c) sam[p].ch[c]
#define fa(p) sam[p].fa 
int cnt[ndsz],ps=1,las=1;
void insert(int c){
    int p=las;
    las=++ps,sam[las].l=sam[p].l+1,cnt[las]=1;
    for(;p&&ch(p,c)==0;p=fa(p))ch(p,c)=las;
    if(p==0)fa(las)=1;
    else{
        if(sam[ch(p,c)].l==sam[p].l+1)fa(las)=ch(p,c);
        else{
            int q=ch(p,c),q1=++ps;
            sam[q1]=sam[q];
            sam[q1].l=sam[p].l+1;
            fa(q)=fa(las)=q1;
            for(;p&&ch(p,c)==q;p=fa(p))ch(p,c)=q1;
        }
    }
}
void build(){
    rep(i,1,n)insert(s[i]-'a');
}
struct te{int t,pr;}edge[ndsz];
int hd[ndsz],pe=1;
void adde(int f,int t){edge[++pe]=(te){t,hd[f]};hd[f]=pe;}

void buildtr(){
    rep(i,2,ps)adde(fa(i),i);
}

猜你喜欢

转载自www.cnblogs.com/ubospica/p/10331750.html
今日推荐