SAM PAM 算法模板
程序员文章站
2024-03-09 08:11:17
...
最近发现我字符串很菜(你这话不对,你不是上个学期就已经是整个机房字符串最菜的吗)。我好像经常忘板子(其实写这篇的时候我已经忘了SA怎么写了)。所以写篇博客吧,若以后再忘可以帮助记忆。
SAM和PAM这两个自动机长得比较像,可以一起记。
这里目前只有基础的版本,只能处理单串问题。广义的版本以后某时再补上(发出咕咕的声音)。
SAM 后缀自动机
如何背板:
记住一个循环:for (; p && !go[p][c]; p = par[p]) go[p][c] = np;
记住一个条件:len[q] == len[p] + 1
记得设根为1,不能用空结点做根
伪代码
设0为空结点,1为根
extend(c) {
新建 np 并设置 len (其实是设置除par外的所有属性,因为go为空)
for (!go[p][c]) go[p][c] = np;
if (!p) par = 1;
else {
par[p] 本应是 q = go[p][c],但必须 len[q] = len[p] + 1
if (len[q] == len[p] + 1) par[p] = q;
else {
新建 nq: 除 len = len[p] + 1 外都与q一样
for (go[p][c] == q) go[p][c] = nq;
par[np] = par[q] = nq;
}
}
p = np;
}
代码
int go[N][26], par[N], len[N], cnt = 1, p = 1;
void extend(int c) {
int np = ++cnt;
len[np] = len[p] + 1;
for (; p && !go[p][c]; p = par[p]) go[p][c] = np;
if (!p) par[np] = 1;
else {
int q = go[p][c];
if (len[q] == len[p] + 1) par[np] = q;
else {
int nq = ++cnt;
len[nq] = len[p] + 1;
par[nq] = par[q];
memcpy(go[nq], go[q], sizeof *go);
for (; p && go[p][c] == q; p = par[p]) go[p][c] = nq;
par[np] = par[q] = nq; //be aware
}
}
p = np;
}
PAM 回文自动机
如何背板:
记住一个循环:while (s[i-len[p]-1] ^ s[i]) p = fail[p];
记得0
根为0号,-1
根为1号。这样可以使其余结点的fail
自动指向0
根
当go[p][c]
为空时新建结点,注意一定要把go[p][c] = np
留到最后来设置。
否则会导致第一个加入的结点fail
指向自己。现象是在插入第二个字符时死循环。
如果模板写挂了,调试最好先输出每次新建结点的fail
和len
,用aaaa
和abacaba
可以排除大部分错。
int go[N][26], fail[N], len[N], np = 1, p = 0;
void init() {len[1] = -1; fail[0] = fail[1] = 1;}
void extend(int i, int c) {
while (s[i-len[p]-1] ^ s[i]) p = fail[p];
int &ch = go[p][c];
if (!ch) {
len[++np] = len[p] + 2;
do p = fail[p];
while (s[i-len[p]-1] ^ s[i]);
fail[np] = go[p][c];
ch = np; //be aware
}
p = ch;
}
上一篇: 安全加密之-PAM
下一篇: 数据结构实验之链表四:有序链表的归并