讲解
本篇博文其实只是笔者个人的做法复习笔记罢了,
如果是为学习后缀自动机不小心点进来的看官们,
我推荐先看 史 上 最 通 俗 的 后 缀 自 动 机 详 解 史上最通俗的后缀自动机详解 史上最通俗的后缀自动机详解,名副其实。
实现
0.Structure
struct SAM{
int s[MAXC],fa;
int len;
SAM(){
memset(s,0,sizeof(s));fa=len=0;}
}sam[MAXN<<1];
1.建立:插入点
新点 c ( c h a r ) c_{(char)} c(char) 插入末端 → l e n n p = + + c n t len_{np=++cnt} lennp=++cnt 赋为 l e n p = l a s t + 1 len_{p=last}+1 lenp=last+1
→ 往 p p p 的父亲找, p = f a p p=fa_p p=fap,沿途把无 s [ c ] s[c] s[c] 的点连向 n p np np ,
然后 →
① ① ① 直到结束无 s [ c ] s[c] s[c], f a n p fa_{np} fanp 赋为 1。
② ② ② 找到第一个 s [ c ] s[c] s[c] 记为 q q q →
C a s e # 1 \;\;\;\;\;\;\;\;\;\;\;\;Case\;\#1\; Case#1 l e n q = = l e n p + 1 len_q==len_p+1 lenq==lenp+1,直接把 f a n p fa_{np} fanp 赋为 q q q。
C a s e # 2 \;\;\;\;\;\;\;\;\;\;\;\;Case\;\#2\; Case#2 l e n q > l e n p + 1 len_q > len_p+1 lenq>lenp+1, e n d p o s n p endpos_{np} endposnp(位置集) 不是 e n d p o s q endpos_q endposq 的子集,不能连父亲,于是把 q q q 拆了:
新建点 n q = + + c n t , s a m [ n q ] = s a m [ q ] nq=++cnt,sam[nq]=sam[q] nq=++cnt,sam[nq]=sam[q],把 q q q 先全部复制到 n q nq nq 上
→ l e n n q = l e n p + 1 len_{nq}=len_p+1 lennq=lenp+1
→ 把 f a q fa_{q} faq 和 f a n p fa_{np} fanp 赋为 n q nq nq
→ 继续往 p p p 的父亲找, p = f a p p=fa_p p=fap ,把 s [ c ] = = q s[c]==q s[c]==q 的改为连向 n q nq nq。
图示:
模板:
int las = 1,cnt = 1;
int f[MAXN<<1]; //extra
void addsam(int c) {
int p = las; int np = (las = ++ cnt);
f[np] = 1; //extra
sam[np].len = sam[p].len + 1;
for(;p && !sam[p].s[c];p = sam[p].fa) sam[p].s[c] = np;
if(!p) sam[np].fa = 1;
else {
int q = sam[p].s[c];
if(sam[q].len == sam[p].len + 1) sam[np].fa = q;
else {
int nq = ++ cnt;sam[nq] = sam[q];
sam[nq].len = sam[p].len + 1;
sam[np].fa = sam[q].fa = nq;
for(;p && sam[p].s[c] == q;p = sam[p].fa) sam[p].s[c] = nq;
}
}
return ;
}
2.处理出现次数
注意到上面注释 //extra
的地方,即是出现次数的初步处理
然后根据 f a fa fa 把 p a r e n t t r e e parent\;tree parenttree 建出来,从叶子开始加上去
模板:
vector<int> g[MAXN<<1];
void build_Parent_Tree() {
for(int i = 2;i <= cnt;i ++) g[sam[i].fa].push_back(i);
}
void dfs(int x) {
for(int i = 0;i < (int)g[x].size();i ++) {
dfs(g[x][i]);
f[x] += f[g[x][i]];
}
}
3.用法
- SAM 和 Parent Tree 高度统一,SAM 上每个点存的出现次数同时也是 Parent Tree 上每一等价类中所有子串出现次数
- Parent Tree 中的父边在字符串匹配时可以当 AC 自动机中的 fail 边用
- 由于 SAM 是个 DAG ,可以进行 DAG 的各种操作,拓扑、DP、…
- ( T o b e c o n t i n u e d ) (To\;be\;continued) (Tobecontinued)