鉴于sam写一次忘一次,在这里记录一下自己对sam的一些小小定义和理解
l
e
n
[
i
]
:
i
节
点
所
代
表
的
字
符
串
中
最
长
的
一
个
的
长
度
len[i]:i节点所代表的字符串中最长的一个的长度
len[i]:i节点所代表的字符串中最长的一个的长度
f
a
[
i
]
:
i
节
点
在
p
a
r
e
n
t
树
上
的
父
亲
,
i
这
个
状
态
的
后
缀
链
接
fa[i]:i节点在parent树上的父亲,i这个状态的后缀链接
fa[i]:i节点在parent树上的父亲,i这个状态的后缀链接
e
d
:
当
前
可
以
接
收
节
点
的
节
点
编
号
(
整
个
字
符
串
在
S
A
M
上
的
状
态
节
点
)
ed:当前可以接收节点的节点编号(整个字符串在SAM上的状态节点)
ed:当前可以接收节点的节点编号(整个字符串在SAM上的状态节点)
c
h
[
i
]
[
c
]
:
i
节
点
加
一
个
字
符
c
后
可
以
转
移
到
哪
里
ch[i][c]:i节点加一个字符c后可以转移到哪里
ch[i][c]:i节点加一个字符c后可以转移到哪里
p
o
s
[
i
]
:
第
i
个
节
点
的
某
个
e
n
d
p
o
s
,
注
意
在
做
第
二
种
类
型
的
转
移
的
时
候
不
要
忘
了
更
新
这
个
pos[i]:第i个节点的某个endpos,注意在做第二种类型的转移的时候不要忘了更新这个
pos[i]:第i个节点的某个endpos,注意在做第二种类型的转移的时候不要忘了更新这个
int fa[maxn],len[maxn],ed=1,ct=1;//这里很重要,构建sam的时候0这个位置代表虚拟状态,需要空出来
map<int,int> ch[maxn];
int pos[maxn]
inline void insert(int c,int i){//这里是在线构建sam的函数
int np=++ct,p=ed;ed=ct;len[np]=len[p]+1;pos[np]=i;
for(;!ch[p][c];p=fa[p])ch[p][c]=np;
if(!p)fa[np]=1;
else{//进入这里说明我们要添加一个已经存在的字符串x+c,其中x是原串的一个后缀,所以我们不能增加再新的转移,而是考虑将新节点接在哪,发现需要接的位置q应该满足len[q]==len[p]+1
int q=ch[p][c];
if(len[q]==len[p]+1)fa[np]=q;//如果满足条件就直接做
else{//但是如果不满足条件,意味着q还对应一些更长的字符串,所以我们新建一个nq把我们需要的转移提出来,再处理一下节点的关系就好了
int nq=++ct;len[nq]=len[p]+1;ch[nq]=ch[q];
pos[nq]=pos[q];//这句话很重要,很容易忘记,记住每一个新节点都需要赋值endpos
fa[nq]=fa[q];fa[q]=fa[np]=nq;
for(;ch[p][c]==q;p=fa[p])ch[p][c]=nq;
}
}
}
高阶技巧:
在建出parent树之后,我们在parent树上dfs时,可能要按照字典序顺序进行dfs (例:2021CCPC东北省赛L题)
此时由于parent树的边上没有转移的字母信息,要怎么得到从
f
a
[
u
]
fa[u]
fa[u]转移来的字母
c
c
c呢?
c
=
=
s
[
p
o
s
[
u
]
−
l
e
n
[
f
a
[
u
]
]
]
c == s[pos[u]-len[fa[u]]]
c==s[pos[u]−len[fa[u]]]
因为我们知道fa[u]节点所代表的字符串是u所代表的节点的字符串的一些后缀,
那么减去其中最长的一个长度,我们就得到了属于从fa[u]转移到u的节点