参考资料
hihoCoder1441
hihoCoder1445
史上最通俗的后缀自动机详解
练习题
hihocoder1449
hihocoder1457
hihocoder1465
hihocoder1413
笔记
字符串
a
a
b
b
a
b
d
aabbabd
aabbabd
模板
struct SuffixAutomaton{
int last=1,cnt=1,len[N],ch[N][26],fa[N];
//sum[i]状态i为起点的子串个数,size[i]状态i的longest()出现次数
int c[N],a[N],size[N],sum[N];
void Insert(int c){
int p=last,np=++cnt; last=np;
len[np]=len[p]+1; size[np]=1;
for (;p && !ch[p][c];p=fa[p]) ch[p][c]=np;
if (!p) {fa[np]=1;return;}
int q=ch[p][c];
if (len[p]+1==len[q]) {fa[np]=q;return;}
int nq=++cnt;
memcpy(ch[nq],ch[q],sizeof ch[q]);
len[nq]=len[p]+1; fa[nq]=fa[q]; fa[q]=fa[np]=nq;
for (;ch[p][c]==q;p=fa[p]) ch[p][c]=nq;
}
//t==0表示相同子串不重复计算,t==1表示重复计算
void Work(){
for (int i=1;i<=cnt;i++) c[len[i]]++;
for (int i=1;i<=cnt;i++) c[i]+=c[i-1];
for (int i=1;i<=cnt;i++) a[c[len[i]]--]=i;
for (int i=cnt;i;i--) if (t) size[fa[a[i]]]+=size[a[i]];
else size[a[i]]=1;
size[1]=0;
for (int i=cnt;i;i--){
sum[a[i]]=size[a[i]];
for (int j=0;j<26;j++)
if (ch[a[i]][j]) sum[a[i]]+=sum[ch[a[i]][j]];
}
}
};
DAG上DP
确定遍历顺序,数组 A[] 为结点遍历顺序
for(int i=1;i<=node;i++) t[len[i]]++;
for(int i=1;i<=node;i++) t[i]+=t[i-1];
for(int i=1;i<=node;i++) A[t[len[i]]--]=i;
一个结点出发的不同子串个数
对于一个节点
i
i
i,
f
[
i
]
f[i]
f[i] 表示从
i
i
i 出发的子串个数(不含空串)。那么,
f
[
i
]
f[i]
f[i] 就等于
∑
(
i
,
j
)
∈
E
d
g
e
(
f
[
j
]
+
1
)
\sum_{(i,j)\in Edge}(f[j]+1)
∑(i,j)∈Edge(f[j]+1) ,
f
[
1
]
f[1]
f[1]即是答案。
转移顺序的话,就按照
A
[
]
A[]
A[] 的倒序
for(int i=cnt;i>=1;i--)
for(int j=0;j<26;j++) if (ch[A[i]][j]) f[A[i]]+=f[ch[A[i]][j]]+1;
一个子串出现的次数等于其所在状态的 e n d p o s endpos endpos 的大小
size[np]=1;
for (int i=cnt;i;i--) size[fa[a[i]]]+=size[a[i]];
一个状态所包含的子串的数量
for(int i=1;i<=cnt;i++)
for(int j=0;j<26;j++) if (ch[a[i]][j]) num[ch[a[i]][j]]+=num[a[i]];
最长公共前缀
int u=1,l=0;
for(int i=1;i<=m;i++){
int c=T[i]-'a';
while(u&&!ch[u][c]) u=fa[u],l=len[u];
if (ch[u][c]) u=ch[u][c],l++; else u=1,l=0;
}
广义后缀自动机
struct SAM {
int ch[N][26],fa[N],len[N],last[N],a[N],t[N],size[N],cnt=1,root=1;
int add(int p, int c) {
if(ch[p][c]&&len[ch[p][c]]==len[p]+1) return ch[p][c];
int np = ++cnt,fg=0;
size[np] = 1; len[np] = len[p] + 1;
while (!ch[p][c] && p) ch[p][c]=np,p=fa[p];
if (!p) {
fa[np] = root;
}else{
int q = ch[p][c];
if (len[p]+1==len[q]) {
fa[np]=q;
}else{
if (len[p]+1==len[np]) fg=1;
int nq = ++cnt;
memcpy(ch[nq], ch[q], sizeof(ch[q]));
len[nq]=len[p]+1;
fa[nq]=fa[q];
fa[q]=fa[np]=nq;
while (p&&ch[p][c]==q) ch[p][c]=nq,p=fa[p];
return fg?nq:np;
}
}
return np;
}
void init() {
//如多次建立自动机,加入memset操作
root = cnt = 1;
}
void build() {
init();
queue<int> q;
q.push(0), last[0] = root;
while (!q.empty()) {
int p=q.front(); q.pop();
for (int i=0;i<trie[p].size();i++) {
last[trie[p][i]] = add(last[p], s[trie[p][i]] - 'A');
q.push(trie[p][i]);
}
}
for (int i=1;i<=cnt;i++) {t[len[i]]++; }
for (int i=1;i<=cnt;i++) {t[i]+=t[i - 1]; }
for (int i=1;i<=cnt;i++) {a[t[len[i]]--] = i; }
for (int i=cnt;i>root;i--) size[fa[a[i]]] += size[a[i]];
}
};