后缀自动机
Posted lhm-
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了后缀自动机相关的知识,希望对你有一定的参考价值。
后缀自动机可以理解为是将字符串所有后缀所建出的(Trie)进行压缩后得出的(DAG)
对于一个子串(s),它结束位置的集合称为(endpos(s)),如对于(aaabbaab),(endpos(ab)={4,8},endpos(bb)={5})
(s_1,s_2)为原串的两个子串,设(|s_1| leqslant |s_2|),则(s_1)是(s_2)的后缀当且仅当(endpos(s_2) subseteq endpos(s_1)),(s_1)不是(s_2)的后缀当且仅当(endpos(s_1) cap endpos(s_2)= varnothing)
如果两个子串的(endpos)相同,则其中子串一个为另一个的后缀
由(endpos)的包含关系可以得出一个树形结构,称为(Parent)树
后缀自动机的节点就是(Parent)树的节点,每个节点表示一个(endpos)
(SAM)的节点数不超过(2n-1),边数不超过(3n-4),数组大小应开成两倍
(len:)为一个(endpos)所对应的子串中最长子串的长度
(ch:)为转移函数
(fa:)为后缀连接
设(minlen)为一个(endpos)所对应的子串中最短子串的长度,得
(len[fa[i]]+1=minlen[i])
后缀自动机是一张有向无环图,其中顶点是状态,而边代表了状态之间的转移
每一个状态包含了它包含的最长子串的一些连续长度的后缀,不是所有后缀,再短的其他后缀在(fa)连接的状态,也就是该串的所有后缀在(Parent)树的链上
从初始状态经由任意路径走到某一终止状态,得到的字符串为原串的某一后缀
从初始状态经由任意路径走到某一状态,得到的字符串为原串的某一子串
所有终止状态包含了原串的所有后缀,所有终止状态又用(fa)相连
如(abbb)
构造(SAM)时有三种情况
① ( aa o aab)
② ( aabb o aabba)
③ ( aab o aabb)
(code:)
void init()
{
las=tot=1;
}
void insert(int c)
{
int p=las,np=las=++tot;
len[np]=len[p]+1;
while(p&&!ch[p][c]) ch[p][c]=np,p=fa[p];
if(!p)
{
fa[np]=1;
return;
}
int q=ch[p][c];
if(len[q]==len[p]+1) fa[np]=q;
else
{
int nq=++tot;
len[nq]=len[p]+1;
fa[nq]=fa[q],fa[np]=fa[q]=nq;
memcpy(ch[nq],ch[q],sizeof(ch[q]));
while(ch[p][c]==q) ch[p][c]=nq,p=fa[p];
}
}
一个状态包含的子串的出现次数可以先拓扑,再用(fa)求出,(siz)初值为(1),(siz[1])应为(0)
(code:)
void calc()
{
for(int i=1;i<=tot;++i) b[len[i]]++;
for(int i=1;i<=tot;++i) b[i]+=b[i-1];
for(int i=1;i<=tot;++i) ord[b[len[i]]--]=i;
for(int i=tot;i;--i)
{
int p=ord[i];
siz[fa[p]]+=siz[p];
}
}
广义后缀自动机,在(trie)上建(SAM)
(code:)
void init()
{
las=tot=pre[0]=1;
}
void insert(int c)
{
int p=las,np=0;
if(!ch[p][c])
{
np=las=++tot;
len[np]=len[p]+1;
}
while(p&&!ch[p][c]) ch[p][c]=np,p=fa[p];
if(!p)
{
fa[np]=1;
return;
}
int q=ch[p][c];
if(len[q]==len[p]+1)
{
if(np) fa[np]=q;
else las=q;
}
else
{
int nq=++tot;
if(!np) las=nq;
len[nq]=len[p]+1;
fa[nq]=fa[q],fa[q]=nq;
if(np) fa[np]=nq;
memcpy(ch[nq],ch[q],sizeof(ch[q]));
while(p&&ch[p][c]==q) ch[p][c]=nq,p=fa[p];
}
}
void bfs(int s)
{
queue<int> q;
q.push(s);
fath[s]=0;
while(!q.empty())
{
int x=q.front();
q.pop();
las=pre[fath[x]];
insert(co[x]);
pre[x]=las;
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to;
if(y==fath[x]) continue;
fath[y]=x;
q.push(y);
}
}
}
......
for(int i=1;i<=n;++i)
if(d[i]==1)
bfs(i);
以上是关于后缀自动机的主要内容,如果未能解决你的问题,请参考以下文章
我的Android进阶之旅关于Android平台获取文件的mime类型:为啥不传小写后缀名就获取不到mimeType?为啥android 4.4系统获取不到webp格式的mimeType呢?(代码片段
我的Android进阶之旅关于Android平台获取文件的mime类型:为啥不传小写后缀名就获取不到mimeType?为啥android 4.4系统获取不到webp格式的mimeType呢?(代码片段