DFA
DFA,即确定性有限状态自动机,由一个五元组M=(Σ,Q,qs,F,tr)M=(\Sigma,Q,q_s,F,tr)M=(Σ,Q,qs,F,tr)组成,其中:
Σ\SigmaΣ为一个有限字符集,其中每个字符ccc称为一个输入符号;
QQQ为一个有限状态集合;
qs∈Qq_s\in Qqs∈Q为初始状态;
F⊆QF\subseteq QF⊆Q称为终结状态集合;
tr∈Q×Σ→Qtr\in Q\times \Sigma\to Qtr∈Q×Σ→Q称为状态转换函数。
tr(q,c)=q′tr(q,c)=q'tr(q,c)=q′表示当前状态为qqq,输入符号为ccc时,自动机MMM将自动转换成下一个状态q′q'q′,此时称q′q'q′为qqq的一个后继状态。
以上全是废话。
大家应该都知道AC自动机吧,如果不知道可以去看这一篇博客:http://blog.csdn.net/wang3312362136/article/details/78659403
一个DFA,就是一个有向图,有一个起点,有一些标有字符的边,有一个或多个终点,从这个起点,按照一个字符串上的字符,按顺序走与字符串的字符相同的边,最终走到了终点,就说明这个DFA能够识别这个字符;如果走到了非法状态qϕq_\phiqϕ,或者走到了一个不是终点的状态,那么说明这个DFA不能识别这个字符串。
对于一个状态ppp和qqq,如果任何一个能够从ppp转换到终点的字符串都可以从qqq转换到终点,反之也成立,那么就说明ppp和qqq是等价状态,记为KaTeX parse error: Unexpected character: '' at position 2: p̲~q。
如果一个DFA中没有等价状态,那么这个自动机叫做最小状态自动机。
SAM
即后缀自动机,是能识别一个串所有后缀的最小状态自动机。
SAM的性质
它有哪些性质呢?
Theorem 1
对于任意一个T∈Σ∗T\in \Sigma^*T∈Σ∗,tr(qs,T)̸=qϕtr(q_s,T)\not=q_\phitr(qs,T)̸=qϕ当且仅当TTT是SSS的一个字串。
Lemma 2
设T∈Σ∗T\in \Sigma^*T∈Σ∗是一个非空字符串,right(T)right(T)right(T)表示TTT在SSS中所有结束位置的集合,则有L(tr(qs,T))={sufr+1∣r∈right(T)}L(tr(q_s,T))=\{suf_{r+1}\mid r\in right(T)\}L(tr(qs,T))={sufr+1∣r∈right(T)}。
Theorem 3
设T1T_1T1,T2T_2T2是SSS的两个非空字符串,则tr(qs,T1)=tr(qs,T2)tr(q_s,T_1)=tr(q_s,T_2)tr(qs,T1)=tr(qs,T2)的充要条件是right(T1)=right(T2)right(T_1)=right(T_2)right(T1)=right(T2)。
Theorem 3.5
记一个状态qqq的rightrightright集合为RqR_qRq,则rightrightright集合恰好为RqR_qRq的串,其长度一定在一个区间内,称之为qqq的合法长度区间,记为[minlq,maxlq][minl_q,maxl_q][minlq,maxlq],对应从初始状态到这个状态的最短长度和最长长度。
Lemma 4
任取两个不同的状态p,qp,qp,q,则下列三式中必有一式成立:
(1)Rp⋂Rq=ϕR_p\bigcap R_q=\phiRp⋂Rq=ϕ (2)Rp⊆RqR_p\subseteq R_qRp⊆Rq (3)Rq⊆RpR_q\subseteq R_pRq⊆Rp
Lemma 5
若一个状态ppp是状态ppp的parentparentparent状态,当且仅当RqR_qRq是最小真包含RpR_pRp的集合,那么q∈Q {qϕ}q\in Q\text{\ }\{q_\phi\}q∈Q {qϕ}的parentparentparent状态存在且唯一。
Theorem 6
设q∈Q {qϕ}q\in Q\text{\ }\{q_\phi\}q∈Q {qϕ},ppp是qqq的parentparentparent状态,则有maxlp=minlq−1maxl_p=minl_q-1maxlp=minlq−1,此时ppp对应的所有字串都是qqq的后缀。
所以,我们在后缀自动机中只需要存储每个节点parentparentparent状态,和maxlmaxlmaxl值即可。
Theorem 7
对串SSS构建后缀自动机MMM,则有MMM的状态数∣Q∣≤2∣S∣+1|Q|\leq 2|S|+1∣Q∣≤2∣S∣+1。
Theorem 8
对串SSS构建后缀自动机MMM,则合法状态转换边的数量不超过3∣S∣3|S|3∣S∣。
- 后缀自动机中只需存储SSS的字串对应的状态;
- 一个状态对应的字串,它们的rightrightright集合相等,且长度取值必定对应一个区间;
- 每个状态与parentparentparent状态的关系必定构成一棵parentparentparent树;
- 一个状态的最小合法长度恰好比parentparentparent状态的最大合法长度多111;
- 后缀自动机是一个线性结构。
证明?
由自己的感觉可得
自己参阅资料吧。(其实可以背结论的)
SAM的构建
增量法,每次在后缀自动机中添加一个字符,然后对当前的SAM进行更新。
显然,添加一个字符并不会造成状态的合并,但有可能造成状态的分离,而分离的状态只有可能是在添加前后缀状态和非后缀状态都可以转换到它,此时,这个状态需要分裂成只能被后缀状态转换的状态和只能被非后缀状态转换的状态。(这个情况之后再讲)
设添加前这个串是SSS,添加的字符为ccc,设tr(qs,S)=ptr(q_s,S)=ptr(qs,S)=p,由于所有后缀节点在parentparentparent树上都是祖先关系,因此把它们记为v1=p,v2,v3,⋯ ,vk=qsv_1=p,v_2,v_3,\cdots ,v_k=q_sv1=p,v2,v3,⋯,vk=qs。
由于tr(qs,Sc)=qϕtr(q_s,Sc)=q_\phitr(qs,Sc)=qϕ,那么我们需要新建一个节点np=tr(qs,Sc)np=tr(q_s,Sc)np=tr(qs,Sc)(这个是显然的)
那么maxlnp=∣S∣+1maxl_{np}=|S|+1maxlnp=∣S∣+1,Rnp′={n+1}R'_{np}=\{n+1\}Rnp′={n+1}。
对于一个后缀状态vvv,如果tr(v,c)=qϕtr(v,c)=q_\phitr(v,c)=qϕ(即vvv没有符号为ccc的合法转换边),那么将tr(v,c)=nptr(v,c)=nptr(v,c)=np即可,正确性显然。
设vpv_pvp是v1,v2,v3,⋯ ,vkv_1,v_2,v_3,\cdots,v_kv1,v2,v3,⋯,vk中第一个存在符号为ccc的合法转换边的状态,那么记qqq为tr(vp,c)tr(v_p,c)tr(vp,c),取r=Rqr=R_qr=Rq中的最小值,此时记Q1=S[r,r],Q2=S[r−1,r],⋯ ,Qr=S[1,r]Q_1=S[r,r],Q_2=S[r-1,r],\cdots,Q_r=S[1,r]Q1=S[r,r],Q2=S[r−1,r],⋯,Qr=S[1,r],下标就是串的长度。
由于S[r]S[r]S[r]显然为ccc,那么记Q1=P0c,Q2=P1c,⋯ ,Qr=Pr−1cQ_1=P_0c,Q_2=P_1c,\cdots,Q_r=P_{r-1}cQ1=P0c,Q2=P1c,⋯,Qr=Pr−1c,显然,Pi=S[r−i,r−1]P_i=S[r-i,r-1]Pi=S[r−i,r−1]。那么状态qqq对应的串就是Qminlq,⋯ ,QmaxlqQ_{minl_q},\cdots,Q_{maxl_q}Qminlq,⋯,Qmaxlq,所有能转换到qqq的状态ppp对应的串就是SQ={Pminlq−1,⋯ ,Pmaxlq−1}S_Q=\{P_{minl_{q}-1},\cdots,P_{maxl_q-1}\}SQ={Pminlq−1,⋯,Pmaxlq−1}。
又由于vpv_pvp对应的后缀串只有SP={P0,⋯ ,Pmaxlvp}S_P=\{P_{0},\cdots,P_{maxl_{v_p}}\}SP={P0,⋯,Pmaxlvp};
讨论qqq的状态:
- 当maxlvp=maxlq−1maxl_{v_p}=maxl_q-1maxlvp=maxlq−1时,显然有SQ⊆SPS_Q\subseteq S_PSQ⊆SP,那么,所有能通过输入符号ccc转换到qqq的状态都是后缀状态,此时qqq不会发生状态的分裂。而对于vpv_pvp在parentparentparent树上的祖先,显然tr(vp,c)tr(v_p,c)tr(vp,c)更不可能发生分裂。
- 当maxlvp<maxlq−1maxl_{v_p}<maxl_q-1maxlvp<maxlq−1时,qqq的合法长度区间可以分成两个部分:[minlq,maxlvp+1][minl_q,maxl_{v_p}+1][minlq,maxlvp+1]以及[maxlvp+2,maxlq][maxl_{v_p}+2,maxl_q][maxlvp+2,maxlq]。前者只有后缀状态能转移到它,后者只有非后缀状态能转移到它。
那么新建一个状态nqnqnq,对应前者,新图中的q′q'q′对应后者。由于RnqR_{nq}Rnq显然只比RqR_{q}Rq多一个∣S∣+1|S|+1∣S∣+1,而∣S∣+1|S|+1∣S∣+1之后并没有字符,没有状态能通过这个转移,那么nqnqnq的状态转换边与qqq完全相同。
而对于vpv_pvp在parentparentparent树上的祖先,显然,如果tr(vp,c)=qtr(v_p,c)=qtr(vp,c)=q则指向nqnqnq,否则不变。
对于npnpnp的parentparentparent:当v[1,n]v_{[1,n]}v[1,n]都不存在ccc的转换边,说明npnpnp是第一次出现,parentparentparent是qsq_sqs。否则,npnpnp的parentparentparent是qqq或者nqnqnq,取决于是否建立了nqnqnq这个状态。
对于q′q'q′和nqnqnq的parentparentparent:如果没有发生分裂,则不改变。如果发生了分裂,设原图中qqq的parentparentparent为parqpar_qparq,则parqpar_qparq,qqq和nqnqnq这三个状态在parentparentparent树上构成祖孙关系,进一步推理得parnq=parq,parq′=nqpar_nq=par_q,par_{q'}=nqparnq=parq,parq′=nq。
代码
struct samnode
{
samnode* tr[26];
samnode* par;
int maxl;
int init(int l=0)
{
memset(tr,0,sizeof tr);
par=NULL;
maxl=l;
return 0;
}
};
struct suffix_automaton
{
samnode* qs;
samnode* qlast;
samnode node[maxn*3];
int cntnode;
inline int clear()
{
delete qs;
delete qlast;
qs=new samnode;
qlast=new samnode;
qs->init();
qlast=qs;
cntnode=0;
return 0;
}
inline int addchr(int ch)
{
samnode* p=qlast;
samnode* np=&node[++cntnode];
np->init(p->maxl+1);
qlast=np;
while((p!=NULL)&&(p->tr[ch]==NULL))
{
p->tr[ch]=np;
p=p->par;
}
if(p==NULL)
{
np->par=qs;
return 0;
}
samnode* q=p->tr[ch];
if(p->maxl+1!=q->maxl)
{
samnode* nq=&node[++cntnode];
nq->init(p->maxl+1);
memcpy(nq->tr,q->tr,sizeof q->tr);
nq->par=q->par;
q->par=nq;
np->par=nq;
while((p!=NULL)&&(p->tr[ch]==q))
{
p->tr[ch]=nq;
p=p->par;
}
}
else
{
np->par=q;
}
return 0;
}
};