算法详解——后缀自动机 - Canopus_wym

Canopus-wym 2021-08-09 原文


算法详解——后缀自动机


DFA


DFA,即确定性有限状态自动机,由一个五元组M=(Σ,Q,qs,F,tr)M=(\Sigma,Q,q_s,F,tr)组成,其中:

Σ\Sigma为一个有限字符集,其中每个字符cc称为一个输入符号;

QQ为一个有限状态集合;

qs∈Qq_s\in Q为初始状态;

F⊆QF\subseteq Q称为终结状态集合;

tr∈Q×Σ→Qtr\in Q\times \Sigma\to Q称为状态转换函数。

tr(q,c)=q′tr(q,c)=q'表示当前状态为qq,输入符号为cc时,自动机MM将自动转换成下一个状态q′q',此时称q′q'qq的一个后继状态。


以上全是废话。

大家应该都知道AC自动机吧,如果不知道可以去看这一篇博客:http://blog.csdn.net/wang3312362136/article/details/78659403

一个DFA,就是一个有向图,有一个起点,有一些标有字符的边,有一个或多个终点,从这个起点,按照一个字符串上的字符,按顺序走与字符串的字符相同的边,最终走到了终点,就说明这个DFA能够识别这个字符;如果走到了非法状态qϕq_\phi,或者走到了一个不是终点的状态,那么说明这个DFA不能识别这个字符串。

对于一个状态ppqq,如果任何一个能够从pp转换到终点的字符串都可以从qq转换到终点,反之也成立,那么就说明ppqq是等价状态,记为KaTeX parse error: Unexpected character: \’\’ at position 2: p̲~q

如果一个DFA中没有等价状态,那么这个自动机叫做最小状态自动机。

SAM

即后缀自动机,是能识别一个串所有后缀的最小状态自动机。

SAM的性质

它有哪些性质呢?


Theorem 1
对于任意一个T∈Σ∗T\in \Sigma^*tr(qs,T)̸=qϕtr(q_s,T)\not=q_\phi当且仅当TTSS的一个字串。
Lemma 2
T∈Σ∗T\in \Sigma^*是一个非空字符串,right(T)right(T)表示TTSS中所有结束位置的集合,则有L(tr(qs,T))={sufr+1∣r∈right(T)}L(tr(q_s,T))=\{suf_{r+1}\mid r\in right(T)\}
Theorem 3
T1T_1T2T_2SS的两个非空字符串,则tr(qs,T1)=tr(qs,T2)tr(q_s,T_1)=tr(q_s,T_2)的充要条件是right(T1)=right(T2)right(T_1)=right(T_2)
Theorem 3.5
记一个状态qqrightright集合为RqR_q,则rightright集合恰好为RqR_q的串,其长度一定在一个区间内,称之为qq的合法长度区间,记为[minlq,maxlq][minl_q,maxl_q],对应从初始状态到这个状态的最短长度和最长长度。
Lemma 4
任取两个不同的状态p,qp,q,则下列三式中必有一式成立:
(1)Rp⋂Rq=ϕR_p\bigcap R_q=\phi (2)Rp⊆RqR_p\subseteq R_q (3)Rq⊆RpR_q\subseteq R_p
Lemma 5
若一个状态pp是状态ppparentparent状态,当且仅当RqR_q是最小真包含RpR_p的集合,那么q∈Q {qϕ}q\in Q\text{\ }\{q_\phi\}parentparent状态存在且唯一。
Theorem 6
q∈Q {qϕ}q\in Q\text{\ }\{q_\phi\}ppqqparentparent状态,则有maxlp=minlq−1maxl_p=minl_q-1,此时pp对应的所有字串都是qq的后缀。
所以,我们在后缀自动机中只需要存储每个节点parentparent状态,和maxlmaxl值即可。
Theorem 7
对串SS构建后缀自动机MM,则有MM的状态数∣Q∣≤2∣S∣+1|Q|\leq 2|S|+1
Theorem 8
对串SS构建后缀自动机MM,则合法状态转换边的数量不超过3∣S∣3|S|


  1. 后缀自动机中只需存储SS的字串对应的状态;
  2. 一个状态对应的字串,它们的rightright集合相等,且长度取值必定对应一个区间;
  3. 每个状态与parentparent状态的关系必定构成一棵parentparent树;
  4. 一个状态的最小合法长度恰好比parentparent状态的最大合法长度多11
  5. 后缀自动机是一个线性结构。

证明?

由自己的感觉可得

自己参阅资料吧。(其实可以背结论的)

SAM的构建

增量法,每次在后缀自动机中添加一个字符,然后对当前的SAM进行更新。


显然,添加一个字符并不会造成状态的合并,但有可能造成状态的分离,而分离的状态只有可能是在添加前后缀状态和非后缀状态都可以转换到它,此时,这个状态需要分裂成只能被后缀状态转换的状态和只能被非后缀状态转换的状态。(这个情况之后再讲)

设添加前这个串是SS,添加的字符为cc,设tr(qs,S)=ptr(q_s,S)=p,由于所有后缀节点在parentparent树上都是祖先关系,因此把它们记为v1=p,v2,v3,⋯ ,vk=qsv_1=p,v_2,v_3,\cdots ,v_k=q_s

由于tr(qs,Sc)=qϕtr(q_s,Sc)=q_\phi,那么我们需要新建一个节点np=tr(qs,Sc)np=tr(q_s,Sc)(这个是显然的)
那么maxlnp=∣S∣+1maxl_{np}=|S|+1Rnp′={n+1}R'_{np}=\{n+1\}

对于一个后缀状态vv,如果tr(v,c)=qϕtr(v,c)=q_\phi(即vv没有符号为cc的合法转换边),那么将tr(v,c)=nptr(v,c)=np即可,正确性显然。

vpv_pv1,v2,v3,⋯ ,vkv_1,v_2,v_3,\cdots,v_k中第一个存在符号为cc的合法转换边的状态,那么记qqtr(vp,c)tr(v_p,c),取r=Rqr=R_q中的最小值,此时记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],下标就是串的长度。

由于S[r]S[r]显然为cc,那么记Q1=P0c,Q2=P1c,⋯ ,Qr=Pr−1cQ_1=P_0c,Q_2=P_1c,\cdots,Q_r=P_{r-1}c,显然,Pi=S[r−i,r−1]P_i=S[r-i,r-1]。那么状态qq对应的串就是Qminlq,⋯ ,QmaxlqQ_{minl_q},\cdots,Q_{maxl_q}所有能转换到qq的状态pp对应的串就是SQ={Pminlq−1,⋯ ,Pmaxlq−1}S_Q=\{P_{minl_{q}-1},\cdots,P_{maxl_q-1}\}

又由于vpv_p对应的后缀串只有SP={P0,⋯ ,Pmaxlvp}S_P=\{P_{0},\cdots,P_{maxl_{v_p}}\}

讨论qq的状态:

  1. maxlvp=maxlq−1maxl_{v_p}=maxl_q-1时,显然有SQ⊆SPS_Q\subseteq S_P,那么,所有能通过输入符号cc转换到qq的状态都是后缀状态,此时qq不会发生状态的分裂。而对于vpv_pparentparent树上的祖先,显然tr(vp,c)tr(v_p,c)更不可能发生分裂。
  2. maxlvp<maxlq−1maxl_{v_p}<maxl_q-1时,qq的合法长度区间可以分成两个部分:[minlq,maxlvp+1][minl_q,maxl_{v_p}+1]以及[maxlvp+2,maxlq][maxl_{v_p}+2,maxl_q]。前者只有后缀状态能转移到它,后者只有非后缀状态能转移到它。
    那么新建一个状态nqnq,对应前者,新图中的q′q'对应后者。由于RnqR_{nq}显然只比RqR_{q}多一个∣S∣+1|S|+1,而∣S∣+1|S|+1之后并没有字符,没有状态能通过这个转移,那么nqnq的状态转换边与qq完全相同。
    而对于vpv_pparentparent树上的祖先,显然,如果tr(vp,c)=qtr(v_p,c)=q则指向nqnq,否则不变。
    对于npnpparentparent:当v[1,n]v_{[1,n]}都不存在cc的转换边,说明npnp是第一次出现,parentparentqsq_s。否则,npnpparentparentqq或者nqnq,取决于是否建立了nqnq这个状态。
    对于q′q'nqnqparentparent:如果没有发生分裂,则不改变。如果发生了分裂,设原图中qqparentparentparqpar_q,则parqpar_qqqnqnq这三个状态在parentparent树上构成祖孙关系,进一步推理得parnq=parq,parq′=nqpar_nq=par_q,par_{q'}=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;
  }
};
发表于
2018-02-22 17:39 
Canopus_wym 
阅读(167
评论(0
编辑 
收藏 
举报

 

版权声明:本文为Canopus-wym原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/Canopus-wym/p/10376228.html

算法详解——后缀自动机 - Canopus_wym的更多相关文章

  1. 关于多线程/进程的一个问题:传递给新线程/进程的参数变量被修改 – 沙漠狼的爱情长跑

      今天在验证tcp服务器多线程/进程代码的时候,出现这样的情况:主线程创建一个新线程/进程,并将一个整形参数 […]...

  2. JDK JRE区别 – joyous jeny

    JDK JRE区别 JDK JRE区别    JDK里面的工具也是用JAVA编写的,它们本身运行的时候也需要一 […]...

  3. cenos6.4安装jdk8 – 利科尔多

    cenos6.4安装jdk8 1.首先 查看CentOS自带JDK是否已安装。 直接输入java -versi […]...

  4. 做个别出心裁的圣诞礼物 – jerry_fuyi

    做个别出心裁的圣诞礼物 用OLED屏显示一个微信动态表情,送给心爱的Ta。 忙活了小半年,转眼间圣诞将至。这次 […]...

  5. 伸展树的基本操作与应用

      blockquote:first-child, #write > div:first-child, […]...

  6. 升级到 Android Studio 3.0 + Gradle 4.1 遇到的一些坑及解决方案

    问题一: Cannot set the value of read-only property \'outpu […]...

  7. UWP 使用新版画中画 FontIcon —— 如何使用自定义字体 —— 简单分析Windows Calculator源代码

    微软在新版UWP计算器中加入了一个“置顶”功能,它相当于我们之前看视频的“画中画”一样。 点击后窗体置顶,同时 […]...

  8. 相似度算法(转载) – wangzhiliang

    相似度算法(转载) 在数据分析和数据挖掘的过程中,我们经常需要知道个体间差异的大小,进而评价个体的相似性和类别 […]...

随机推荐

  1. 大型猎头管理系统源代码(转)

      国内一流UI设计师布局设计,整体采用Bootstrap & Ajax框架,大方整洁; 操控灵活、扩 […]...

  2. windows 命令提示符如何进入C盘路径,从C盘如何进入D盘,完成后怎样返回系统默认的路径

    Microsoft Windows XP [版本 5.1.2600](C) 版权所有 1985-2001 Mi […]...

  3. mac电脑下使用fcrackzip破解zip压缩文件密码

    fcrackzip简介 fcrackzip是一款专门破解zip类型压缩文件密码的工具,工具小巧方便、破解速度快 […]...

  4. 三种主流芯片架构–arm,mips,x86

    arm,mips,x86 三种芯片构架比较 1. ARM ARM是高级精简指令集的简称(Advanced RI […]...

  5. Groovy中如何向已有的类添加新方法

    Groovy 中有多种途径实现向原有类添加方法,具体有如下几种: MOP(meta object protoc […]...

  6. 北京工作居住证办理条件及用途

     一、办理条件本市有固定住所且具备下列条件之一者,均可申请《工作居住证》: (一)具有2年以上工作经历并取得学 […]...

  7. 9.28网页设计小作业

    网页设计小作业 目录 网页设计小作业 1.效果图 2.要求 3.第一步实现图片与超链接 3.第二步实现文字内容 […]...

  8. 面试题——数组转树结构

    树结构大家应该都比较熟悉,这里我主要说两种:一个根节点和多个根节点。 一个根节点,就像我们的html节点,不可 […]...

展开目录

目录导航