浅谈后缀自动机SAM
之前学过,但是一直半懂不懂,只会背板子
现在甚至连板子都不会背了
还是从新学一遍比较好
endpos
定义一个子串的 e n d p o s endpos endpos为它在原串中结束位置的集合
原串:
a
b
a
b
c
b
ababcb
ababcb
e
n
d
p
o
s
(
a
b
)
=
{
2
,
4
}
,
e
n
d
p
o
s
(
b
)
=
{
2
,
4
,
6
}
endpos(ab) = \{2,4\},endpos(b)=\{2,4,6\}
endpos(ab)={2,4},endpos(b)={2,4,6}
把
e
n
d
p
o
s
endpos
endpos相同的子串称为一个类
有几个显然的性质
- 如果一个子串 a a a的 e n d p o s endpos endpos包含子串 b b b的,那么 a a a一定是 b b b的后缀
- 如果两个子串的 e n d p o s endpos endpos有交,那么一定是一个包含另一个,且子串是后缀关系
- 对于一个类,设最大长度为 m a x max max,最小的为 m i n min min,那么长度在 [ m i n , m a x ] [min,max] [min,max]中的都存在
parent tree(后缀link)
根据
e
n
d
p
o
s
endpos
endpos的子集关系建一颗树
可以发现这棵树正好能满足SAM的所有性质,就很虚浮
点边都是
O
(
n
)
O(n)
O(n)的
同样可以得到一个重要的性质
- 假设
m
i
n
(
x
)
,
m
a
x
(
x
)
min(x),max(x)
min(x),max(x)表示这个类能表示的最短/长子串长度,可以得到
m
i
n
(
x
)
=
m
a
x
(
f
a
)
+
1
min(x)=max(fa)+1
min(x)=max(fa)+1
挺显然的
把上面绿色的节点称做终止链
构造
可结合图片&代码来理解
结构体的部分本别表示转移边,最大长度和后缀link
struct A {
int ch[27], len, fa;
} a[N << 2];
int lst = 1, tot = 1, n;
void insert(int c, int id) {
int p = lst, np = ++ tot; lst = np;
a[np].len = a[p].len + 1;//接上终止链
while(p && !a[p].ch[c]) a[p].ch[c] = np, p = a[p].fa;//把终止链上的转移边加上
if(!p) a[np].fa = 1;
else {
int q = a[p].ch[c];//找到第一个可以转移的,考虑np和它合并
if(a[q].len == a[p].len + 1) a[np].fa = q;//如果中间没有其他串,那后缀link指向它
else {
int clone = ++ tot; a[clone] = a[q];//不然就把这个类拆开成两部分
a[clone].len = a[p].len + 1;
while(p && a[p].ch[c] == q) a[p].ch[c] = clone, p = a[p].fa;//把信息继承过来
a[np].fa = a[q].fa = clone;
}
}
}
大概就这样,具体用法多做题即可
有时间再补