后缀自动机SAM笔记和模板

以下是模板:

#include<stdio.h>
#include<string.h>

//后缀自动机Suffix Automaton
/*
  注意:
  (1)CHARSZ是字符个数 idx()可以对字符进行编码。默认只接受小写字母
  (2)调用SAM::buildSAM即可建树
  (3)调用SAM::clearSAM清空内存。
*/
#define CHARSZ 26
int idx(char c){return c-'a';}

struct State{
  State* par,*go[CHARSZ];
  int val,f;
  //method
  State(int _val){
    par=0;
    val=_val;
    memset(go,0,sizeof(go));
  }
  void clear_all(){
    for (int i=0;i<CHARSZ;i++)
        if(go[i]) go[i]->clear_all();
    delete this;
  }
}*root,*last;
class SAM{
  private:
    State* root;
    State* last;
    void extend(int w);
  public:
    SAM(){root=last=NULL;}
    void buildSAM(char* s);   //初始化SAM
    void clearSAM();          //释放SAM
    State* getRoot(){return root;}
};
void SAM::extend(int w){
  State* p=last;
  State* np=new State(p->val+1);
  while(p&&p->go[w]==0){
    p->go[w]=np;
    p=p->par;
  }
  if (p==0) np->par=root;
  else{
    State* q=p->go[w];
    if (p->val+1 == q->val) np->par=q;
    else{
        State* nq=new State(p->val+1);
        memcpy(nq->go,q->go,sizeof(q->go));
        nq->par=q->par;
        q->par=nq;
        np->par=nq;
        while (p&& p->go[w]==q){
            p->go[w]=nq;
            p=p->par;
        }
    }
  }
  last=np;
}
void SAM::buildSAM(char* s){
  root=last=new State(0);
  int slen=strlen(s);
  for (int i=0;i<slen;i++)
    extend(idx(s[i]));
}
void SAM::clearSAM(){if (root) root->clear_all(); root=NULL;}
//-----------------end---------------------
int main(){
  SAM sam;
  sam.buildSAM("hello,world");
  //以下代码判断是否是hello,world的子串。
  while (true){
    char s[100];
    scanf("%s",s);
    State* cur=sam.getRoot();
    for (int i=0;i<strlen(s);i++){
      cur=cur->go[idx(s[i])];
      if (!cur)break;
    }
    if (cur)printf("yes\n");else printf("no\n");
  }
  return 0;
}


以上述图为例

STR=aabbabd

一、节点、蓝色边、绿色边概念

  节点个数是线性O(n),边个数也是线性的。

   每个节点接受一个STR的子串集合,SAM接受(即走蓝色边)且仅接受STR所有的子串。


节点x接受的子串性质:

(1) 这些子串总是相互为后缀

(2) 令最长子串为t1,t2,...tk,最多的为tp,tp+1...tk。

     则所有子串为ti,ti+1,....,tk,1<=i<=p。 

     如节点6,包含的所有子串为:ba,bba,abba,aabba。

(3) suffix links

     绿色边,某个状态中,最长子串某个后缀如果不在此状态表示的集合中,则用suffix links连接过去。

    如节点6,包含的所有子串为:ba,bba,abba,aabba。

    还有两个子串 "a" 和 "" 就是通过绿色边连接的:6->1->0。

二、线性构造算法

   令STR=aabbabd

   1、由前i个字符构造的SAM(i)和前i+1个字符构造的SAM(i+1) 两个后缀自动机中:

       有:(1)SAM(i)是SAM(i+1)的子图,故直接在SAM(i)上扩展即可得到SAM(i+1)

              (2)SAM(i) 接受 STR[1..i]的所有子串,故SAM(i+1)只需要添加东西,使之接受所有以STR[i+1]结尾的子串即可。

                   另外,由于新添的字符,可能导致原先节点的集合发生分裂,故需要在适当的时候,将right集合发送变动的节点拆分成两个节点。

      例子:SAM(5) 接受aabba的所有的子串,

             SAM(6)需要再SAM(5)的基础上,额外接受b,ab,bab,bbab,abbab,aabbab这六个子串,即需要在

             又知道这6个子串只需要从节点6、节点1,节点S 各扩展1条b出去即可。

             [1] 节点6(ba,bba,abba,aabba)+b= (bba,bbab,abbab,aabbab)

             [2]节点1(a)+b=(ab)

             [3]节点S("")+b=(b)

             而6->1->S 恰好是图中的绿色边(也就是fail指针),故在从节点6扩展出去时,只需要不断走fail指针,然后扩展,直到处理完节点S。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值