ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

广义SAM

2022-03-08 18:32:51  阅读:196  来源: 互联网

标签:SAM int tr len lst 广义 np nq


参考
还有首先你要会SAM吧~

用途

相比与单串SAM,广义自动机能存储的是多个字符串

有两种写法,第一种是离线利用trie树结构,第二种是在线伪广义SAM

离线+Trie

首先构建出trie树。
然后在trie树上BFS(),用\(pos[u]\)映射trie树上\(u\)节点对应SAM上的节点。

为什么不dfs,因为时间复杂度是trie树上所有叶子到根的距离和,证明BFS\(O(n)\)复杂度具体见上面参考博客

code:

struct SAM {
  int tr[N<<1][M],nd,len[N<<1],par[N<<1],pos[N*M];
  queue<int> Q;
  int Insert(int x,int lst) {
        ...略,返回新节点编号
  }
  void BFS() {
      Q.push(1);pos[1]=1;
      while(!Q.empty()) {
          int u=Q.front(); Q.pop();
          for(int i=0;i<26;i++) {
              int v=T.go[u][i];
              if(!v)continue;
              pos[v]=Insert(i,pos[u]);
              Q.push(v);
          }
       }
}S;

相信上面code的你能够很好的理解

在线

虽然它很伪,但通常跑的比上面那种快,而且对于很多问题在上面处理起来很方便。
方便讲解,先放一份原来插单串的code:

int Insert(int x,int lst) {
    int p=lst,np=++nd;len[np]=len[p]+1;
    for(;!tr[p][x];p=par[p])tr[p][x]=np;
    if(!p) {par[np]=1;}
    else {
        int q=tr[p][x];
        if(len[q]==len[p]+1) {par[np]=q;}
        else {
            int nq=++nd;par[nq]=par[q];len[nq]=len[p]+1;
            for(int j=0;j<26;j++)tr[nq][j]=tr[q][j];
            par[q]=par[np]=nq;
            for(;tr[p][x]==q;p=par[p]) tr[p][x]=nq;
        }
    }
    return np;
}

多串总体的区别就是每加入一个新串前让lst=1(root)
这样会出现之前插单串没出现过的情况:之前p=lst往上找tr[p][x]!=0之前肯定会存在tr[p][x]=0
然而现在可能一开始tr[lst][x]!=0,这有什么问题吗?就跟原来一样分两类讨论(拆点或不拆点)
问题就在于,这时新加的np是个空点,因为它是完全没有必要的。
令q=tr[lst][x]

  • 如果len[q]=len[lst]+1,那此时直接返回q即可。
  • 否则len[q]>len[lst]+1,还是要拆出nq,但是在(上面拆点)代码中唯一用到新点(np)的就是par[np]=nq
    可len[nq]=len[np]的,nq已经包含了np了……
    So Easy!直接不创np这个空点不就行了
    我们上面也说明了现在新代码的写法。先判断tr[lst][x]!=0时就不定义新np点……最后return nq,否则写法跟上面单串一样的。
  • code
struct SAM {
    int tr[N<<1][M],nd,len[N<<1],par[N<<1];
    queue<int> Q;
    SAM() {nd=1;}
    int Insert(int x,int lst) {
        int p=lst;
        if(tr[p][x]) {
            int q=tr[p][x];
            if(len[q]==len[p]+1)return q;
            int nq=++nd;par[nq]=par[q];len[nq]=len[p]+1;
            for(int j=0;j<c;j++)tr[nq][j]=tr[q][j];
            for(;tr[p][x]==q;p=par[p]) tr[p][x]=nq;
            return par[q]=nq;
        }
        int np=++nd;len[np]=len[p]+1;
        for(;!tr[p][x];p=par[p])tr[p][x]=np;
        if(!p) {par[np]=1;}
        else {
            int q=tr[p][x];
            if(len[q]==len[p]+1) {par[np]=q;}
            else {
                int nq=++nd;par[nq]=par[q];len[nq]=len[p]+1;
                for(int j=0;j<c;j++)tr[nq][j]=tr[q][j];
                par[q]=par[np]=nq;
                for(;tr[p][x]==q;p=par[p]) tr[p][x]=nq;
            }
        }
        return np;
    }
}S;

ps.最后强调一点:每个结点中的子串可能来自多个字符串。然而所有来源于相同字符串的子串endpos集合保证相同,而来源于不同字符串的endpos不一定相同.
很好理解(后面加一堆废话),假如加入了i-1个字符串(此时SAM满足上面性质)。第i个字符串的加入,会拆分原来的点(拆点不影响上面性质,只不过是对于同一个字符串同一个等价类被拆成多个节点罢了)。新加的i字符串肯定是满足的。

标签:SAM,int,tr,len,lst,广义,np,nq
来源: https://www.cnblogs.com/bestime/p/15981865.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有