【模板】后缀自动机

#include<bits/stdc++.h>
using namespace std;
const int CHARSET_SIZE=26;
struct Suffix_Automaton{
  struct Node{
    Node *ch[CHARSET_SIZE],*prt;                //prt是后缀链接
    int maxl;                                   //当前节点表示的字串的最大长度
    Node(int maxl=0):ch(),prt(NULL),maxl(maxl){}
    int getMin(){return prt->maxl+1;}           //v->min=v->prt->maxl+1
  }*root,*last;                                 //root表示parent树的根/起始节点 last表示整个母串
  void init(){root=last=new Node;}
  Node *extend(int c){                          //扩展SAM
    Node *u=new Node(last->maxl+1),*v=last;     //u表示新的母串
    for(;v&&!v->ch[c];v=v->prt)v->ch[c]=u;      //将last的后缀连接路径上没有字符c出边的v连向u
    if(!v){u->prt=root;}                        //如果v跳到了NULL 需要把u连向parent树的根
    else if(v->ch[c]->maxl==v->maxl+1){
      u->prt=v->ch[c];                          //把u连接到trans(v,c)    
    }else{                                      //需要新建节点
      Node *n=new Node(v->maxl+1),*o=v->ch[c];  //n是new o是old
      copy(o->ch,o->ch+CHARSET_SIZE,n->ch);     //复制出边到新节点
      n->prt=o->prt;                            //n的后缀链接指向o的后缀连接
      o->prt=u->prt=n;                          //o和u的后缀链接指向n
      for(;v&&v->ch[c]==o;v=v->prt)v->ch[c]=n;  //把路径上原来有转移的o的节点改成指向n
    }
    last=u;                                     //替换整个母串
    return u;
  }
}sam;
int main(){
  sam.init();
  return 0;
}

如果你还需要计算right集合的大小,那么就加上内存池吧

#include<bits/stdc++.h>
using namespace std;
#define N 2000010
const int CHARSET_SIZE=26;
struct Suffix_Automaton{
  struct Node{
    Node *ch[CHARSET_SIZE],*prt;                    //prt是后缀链接
    int maxl,right;                                 //maxl当前节点表示的字串的最大长度 right集合大小
    Node(int maxl=0,int news=0):ch(),prt(NULL),maxl(maxl),right(news){}
    int getMin(){return prt->maxl+1;}               //v->min=v->prt->maxl+1
  }*root,*last,pool[N],*cur;                        //root表示parent树的根/起始节点 last表示整个母串 内存池方便遍历
  void init(){cur=pool;root=last=new (cur++)Node;}
  void extend(int c){                               //扩展SAM
    Node *u=new (cur++)Node(last->maxl+1,1),*v=last;//u表示新的母串
    for(;v&&!v->ch[c];v=v->prt)v->ch[c]=u;          //将last的后缀连接路径上没有字符c出边的v连向u
    if(!v){u->prt=root;}                            //如果v跳到了NULL 需要把u连向parent树的根
    else if(v->ch[c]->maxl==v->maxl+1){
      u->prt=v->ch[c];                              //把u连接到trans(v,c) 
    }else{                                          //需要新建节点
      Node *n=new (cur++)Node(v->maxl+1,0),*o=v->ch[c]; //n是new o是old
      copy(o->ch,o->ch+CHARSET_SIZE,n->ch);         //复制出边到新节点
      n->prt=o->prt;                                //n的后缀链接指向o的后缀连接
      o->prt=u->prt=n;                              //o和u的后缀链接指向n
      for(;v&&v->ch[c]==o;v=v->prt)v->ch[c]=n;      //把路径上原来有转移的o的节点改成指向n
    }
    last=u;                                         //替换整个母串
  }
  vector<Node*> topo;
  void toposort(){                                  //按照maxl从小到大排序
    static int buc[N];
    int maxv=0;
    for(Node *p=pool;p!=cur;p++){
      maxv=max(maxv,p->maxl);
      buc[p->maxl]++;
    }
    for(int i=1;i<=maxv;i++)buc[i]+=buc[i-1];
    topo.resize(cur-pool);
    for(Node *p=pool;p!=cur;p++)topo[--buc[p->maxl]]=p;
    fill(buc,buc+maxv+1,0);                         //清空
  }
  void cal_right(){
    toposort();
    for(int i=topo.size()-1;i>0;i--){               //递推right按照maxl从大到小
      Node *v=topo[i];
      v->prt->right+=v->right; 
    }
  }
}sam;
int main(){
  sam.init();
  return 0;
}

如果你用不来指针或者不方便用指针,莫慌,这里有数组版

#include<bits/stdc++.h>
using namespace std;
#define N 2000010
const int CHARSET_SIZE=26;
struct Suffix_Automaton{
  struct Node{
    int ch[CHARSET_SIZE],prt;                             //prt是后缀链接
    int max;                                              //当前节点表示的字串的最大长度
    Node(int max=0):ch(),prt(0),max(max){}
  }t[N];
  int cnt,root,last;                                      //root表示parent树的根/起始节点 last表示整个母串
  int getMin(int u){return t[t[u].prt].max+1;}            //v->min=v->prt->max+1
  int newnode(int max=0){t[++cnt]=Node(max);return cnt;}
  void init(){cnt=0;root=last=newnode();}
  void extend(int c){                                     //扩展SAM
    int u=newnode(t[last].max+1),v=last;                  //u表示新的母串
    for(;v&&!t[v].ch[c];v=t[v].prt)t[v].ch[c]=u;          //将last的后缀连接路径上没有字符c出边的v连向u
    if(!v){t[u].prt=root;}                                //如果v跳到了0 需要把u连向parent树的根
    else if(t[t[v].ch[c]].max==t[v].max+1){
      t[u].prt=t[v].ch[c];                                //把u连接到trans(v,c)
    }else{                                                //需要新建节点
      int n=newnode(t[v].max+1),o=t[v].ch[c];             //n是new o是old
      memcpy(t[n].ch,t[o].ch,sizeof(t[o].ch));            //复制出边到新节点
      t[n].prt=t[o].prt;                                  //n的后缀链接指向o的后缀连接
      t[o].prt=t[u].prt=n;                                //o和u的后缀链接指向n
      for(;v&&t[v].ch[c]==o;v=t[v].prt)t[v].ch[c]=n;      //把路径上原来有转移的o的节点改成指向n
    }
    last=u;                                               //替换整个母串
  }
}sam;
int main(){
  sam.init();
  return 0;
}

数组+right集合大小维护

#include<bits/stdc++.h>
using namespace std;
#define N 2000010
const int CHARSET_SIZE=26;
struct Suffix_Automaton{
  struct Node{
    int ch[CHARSET_SIZE],prt;                               //prt是后缀链接
    int maxl,right;                                         //maxl当前节点表示的字串的最大长度 right集合大小
    Node(int maxl=0,int news=0):ch(),prt(0),maxl(maxl),right(news){}
  }t[N];
  int cnt,root,last;                                        //root表示parent树的根/起始节点 last表示整个母串 内存池方便遍历
  int getMin(int u){return t[t[u].prt].maxl+1;}             //v->min=v->prt->maxl+1
  int newnode(int maxl=0,int news=0){t[++cnt]=Node(maxl,news);return cnt;}
  void init(){cnt=0;root=last=newnode();}
  void extend(int c){                                       //扩展SAM
    int u=newnode(t[last].maxl+1,1),v=last;                 //u表示新的母串
    for(;v&&!t[v].ch[c];v=t[v].prt)t[v].ch[c]=u;            //将last的后缀连接路径上没有字符c出边的v连向u
    if(!v){t[u].prt=root;}                                  //如果v跳到了0 需要把u连向parent树的根
    else if(t[t[v].ch[c]].maxl==t[v].maxl+1){
      t[u].prt=t[v].ch[c];                                  //把u连接到trans(v,c)
    }else{                                                  //需要新建节点
      int n=newnode(t[v].maxl+1,0),o=t[v].ch[c];            //n是new o是old
      memcpy(t[n].ch,t[o].ch,sizeof(t[o].ch));              //复制出边到新节点
      t[n].prt=t[o].prt;                                    //n的后缀链接指向o的后缀连接
      t[o].prt=t[u].prt=n;                                  //o和u的后缀链接指向n
      for(;v&&t[v].ch[c]==o;v=t[v].prt)t[v].ch[c]=n;        //把路径上原来有转移的o的节点改成指向n
    }
    last=u;                                                 //替换整个母串
  }
  }
  int topo[N];
  void toposort(){                                          //按照maxl从小到大排序
    static int buc[N];
    int maxv=0;
    for(int i=1;i<=cnt;i++){
      buc[t[i].maxl]++; 
      maxv=max(maxv,t[i].maxl);
    }
    for(int i=1;i<=maxv;i++)buc[i]+=buc[i-1];
    for(int i=1;i<=cnt;i++)topo[buc[t[i].maxl]--]=i;
    fill(buc,buc+maxv+1,0);
  }
  void cal_right(){
    toposort();
    for(int i=cnt;i>=1;i--){                                //递推right按照maxl从大到小
      Node v=t[topo[i]];
      t[v.prt].right+=v.right;
    }
  }
}sam;
int main(){
  sam.init();
  return 0;
}
原文地址:https://www.cnblogs.com/dream-maker-yk/p/9683411.html