讲解
本篇博文其实只是笔者个人的做法复习笔记罢了,
如果是为学习后缀自动机不小心点进来的看官们,
我推荐先看 史 上 最 通 俗 的 后 缀 自 动 机 详 解 史上最通俗的后缀自动机详解 史上最通俗的后缀自动机详解,名副其实。
实现
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)