后缀自动机SAM
参见sxTQX的博客
后缀自动机的性质
本质不同子串个数
实际上是后缀自动机的DAG上DP,求DAG的路径数
但是我们有不需要DP的做法
答案为
[sum_{i=2}^{tot}len[i]-len[fa[i]]
]
设这个节点的最短长度为(minlen_u),最长长度为(len_u),那么这个节点包含的不同子串数是(maxlen-minlen+1)。
注意到(minlen_u=maxlen_{fa[u]}+1),就可以得到上面的结论了
其他的很多问题都可以在DAG上DP完成
求LCS
即为(parent)树上的(LCA)的(len)
广义后缀自动机 广义SAM
离线版
将所有模式串插入Trie,对Trie进行bfs依次插入,插入过程与SAM基本相同,只不过要从Trie树上的父亲在SAM中对应的节点往后插入
namespace SAM{
int ch[N][26],fa[N],len[N],tot=1;
inline int insert(int last,int a){
int cur=last,id=++tot;
len[id]=len[cur]+1;
for(;cur&&!ch[cur][a];cur=fa[cur])ch[cur][a]=id;
if(!cur){fa[id]=1;return id;}
int q=ch[cur][a];
if(len[q]==len[cur]+1){fa[id]=q;return id;}
int nq=++tot;fa[nq]=fa[q];fa[id]=fa[q]=nq;
for(int i=0;i<26;++i)ch[nq][i]=ch[q][i];
len[nq]=len[cur]+1;
for(;cur&&ch[cur][a]==q;cur=fa[cur])ch[cur][a]=nq;
return id;
}
}
namespace Trie{
int ch[N][26],rt=1,tot=1,fa[N];
inline void insert(char *s){
int n=strlen(s+1),a,cur=rt;
for(int i=1;i<=n;++i){
a=s[i]-'a';
if(!ch[cur][a])ch[cur][a]=++tot,fa[tot]=cur;
cur=ch[cur][a];
}
}
queue<int> q;
int pos[N];
inline void build(){
q.push(1);pos[1]=1;
while(!q.empty()){
int u=q.front();q.pop();
for(int i=0;i<26;++i)
if(ch[u][i]){
q.push(ch[u][i]);
pos[ch[u][i]]=SAM::insert(pos[u],i);
}
}
}
}
在线版
咕咕咕