1. 程式人生 > 其它 >廣義SAM

廣義SAM

參考
還有首先你要會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;