zoukankan      html  css  js  c++  java
  • 【模板】后缀自动机

    #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;
    }
    
  • 相关阅读:
    Flask下如何处理Requests 上传中文文件名的问题
    xml.etree.ElementTree对CDATA的输出
    Java 实现HTML富文本导出至word完美解决方案
    tornado学习笔记18 _RequestDispatcher 请求分发器
    哗啦啦Python之路
    哗啦啦Python之路
    哗啦啦python金融量化之路
    哗啦啦Python之路
    哗啦啦Python之路
    哗啦啦Python之路
  • 原文地址:https://www.cnblogs.com/dream-maker-yk/p/9683411.html
Copyright © 2011-2022 走看看