【整理】如何選取后綴數組&&后綴自動機


 后綴家族已知成員

        后綴樹
        后綴數組
        后綴自動機
        后綴仙人掌
        后綴預言
        后綴Splay ?
后綴樹是后綴數組和后綴自動機的祖先? 功能還是比較強大的,在回文串或者字典序方面還是有用處。 而且現在已經有了線性的建樹方法。
(但其實我也沒用過后綴樹。)下面對比后綴自動機和后綴數組
 
 
  • 單個字符串問題                                                                  不等號是“優於”,&&是差不多(以下是個人感覺)

    • 1重復子串
      • 1,1 可交叉最長重復子串                              后綴自動機>=后綴數組                          都是基本題,但是前者代碼稍短
      • 1,2 不可交叉最長重復子串                          后綴數組>=后綴自動機                          前者易於判斷交叉;后者則需要記錄每個狀態所有出現的位置
      • 1,3 可交叉的k次最長重復子串                     后綴自動機>=后綴數組                          后者需+二分判定;前者無需判斷,直接拓撲出每個狀態的次數                     
    • 2子串個數問題
      • 2,1 不相同子串個數                                     后綴自動機&&后綴數組                          都是基本功能,易於實現。
    • 3循環子串問題
      • 3,1 求最小循環節                                         后綴數組,kmp                                           后綴自動機應該不行。
      • 3,2 重復次數最多的連續重復子串                后綴數組
  • 兩個字符串串問題

    • 1公共子串問題 
      • 1,1 最長公共子串                                         后綴自動機&&后綴數組                          都是基本功能
    • 2子串個數問題
      • 2,1 特定長度的公共子串                              后綴自動機&&后綴數組                           二者的基本功能
  • 多個字符串問題

    • 1公共子串問題
      • 1,1 在k個字符串中的出現的最長子串           廣義后綴自動機>=后綴數組(KMP也可以求多個串的最長公共字串)               (具體效率誰高取決於數據)
      • 1,2 在每個字符串中出現k次的最長子串       廣義后綴自動機>=后綴數組
      • 1,3 在每個字符串中或反轉后出現的最長子串 廣義后綴自動機?后綴數組            
  • 其他

  • 最小表示法: 后綴自動機                
  • 最小循環節 :后綴數組

個人感覺:

單串和兩串的題,基本上用后綴數組或者后綴自動機都可以實現。多串的題用廣義后綴自動機也是非常強的,有點題如果要用后綴數組,則必須要用RMQ(樹狀數組||ST)+二分,甚至要用Splay來解決。當然靈活的運用后綴數組加各種工具來解決問題,才能應對各種難題,畢竟后綴自動機也是有局限的。個人更傾向於寫后綴自動機,感覺好實現一點,代碼也好看一點。

下面對比一下多串字符串的處理

廣義后綴自動機的題:

             

POJ3294:  題意:給定一些模板字符串,求一個最長公共字串,這個最長公共字串至少在一半以上的字符串里出現過。

                    對比:如果是后綴數組,則要+二分+RMQ;而廣義后綴自動機只需要記錄出現的位置,最后傳遞即可。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<memory>
#include<cmath>
#define maxn 350003
using namespace std;
int n,len,ans,Max,now;
char s[1010],cap[1010];
struct SAM
{
    int ch[maxn][26],fa[maxn],maxlen[maxn],Last,sz;
    int root,nxt[maxn],size[maxn];
    void init()
    {
        sz=0;
        root=++sz;
        memset(size,0,sizeof(size));
        memset(ch[1],0,sizeof(ch[1]));
        memset(nxt,0,sizeof(nxt));
    }
    void add(int x)
    {
        int np=++sz,p=Last;Last=np;
        memset(ch[np],0,sizeof(ch[np]));
        maxlen[np]=maxlen[p]+1;
        while(p&&!ch[p][x]) ch[p][x]=np,p=fa[p];
        if(!p) fa[np]=1;
        else {
            int q=ch[p][x];
            if(maxlen[p]+1==maxlen[q]) fa[np]=q;
            else {
                int nq=++sz;
                memcpy(ch[nq],ch[q],sizeof(ch[q]));size[nq]=size[q]; nxt[nq]=nxt[q];
                maxlen[nq]=maxlen[p]+1;
                fa[nq]=fa[q];
                fa[q]=fa[np]=nq;
                while(p&&ch[p][x]==q) ch[p][x]=nq,p=fa[p];
            }
        }
        for(;np;np=fa[np]) 
          if(nxt[np]!=now) {
              size[np]++;
              nxt[np]=now;
          }else break;
    }
    void dfs(int x,int d){//輸出    
       if(d!=maxlen[x] || d>ans) return;
       if(maxlen[x]==ans && size[x]>n){ puts(cap); return; }
          for(int i=0;i<26;++i)
          if(ch[x][i]){ cap[d]=i+'a'; dfs(ch[x][i],d+1); cap[d]=0; }
    }
};
SAM Sam;
int main()
{
    while(~scanf("%d",&n)&&n){     
        Sam.init();
        for(int i=1;i<=n;i++) {
            scanf("%s",s+1);
            Sam.Last=Sam.root;
            len=strlen(s+1);
            now=i;
            for(int j=1;j<=len;j++) Sam.add(s[j]-'a');
        }
        Max=0;ans=0;n>>=1;
        for(int i=1;i<=Sam.sz;i++) 
            if(Sam.size[i]>n&&Sam.maxlen[i]>ans) { Max=i;ans=Sam.maxlen[i];}
        if(ans) Sam.dfs(1,0);
        else puts("?");
        puts("");
    }
    return 0;
}
View Code

SPOJ8093  題意:給定一些模板串,詢問每個匹配串在多少個模板串里出現過。

                    對比:同上。傳遞的兩種方式:每加一個字符傳遞一次;也可以用bitset記錄在哪里出現過等到加完所有字符串后再拓撲排序,然后“亦或”向上傳遞。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define N 200003
using namespace std;
int ch[N][30],fa[N],l[N],n,m,len;
int r[N],v[N],cnt,np,p,nq,q,last,root,nxt[N],now,size[N];
char s[N];
void extend(int x)
{
    int c=s[x]-'a';
    p=last; np=++cnt; last=np; 
    l[np]=l[p]+1;
    for (;p&&!ch[p][c];p=fa[p]) ch[p][c]=np;
    if (!p) fa[np]=root;
    else {
        q=ch[p][c];
        if (l[q]==l[p]+1) fa[np]=q;
        else {
            nq=++cnt; l[nq]=l[p]+1;
            memcpy(ch[nq],ch[q],sizeof ch[nq]); size[nq]=size[q]; nxt[nq]=nxt[q];
            fa[nq]=fa[q];
            fa[q]=fa[np]=nq;
            for (;ch[p][c]==q;p=fa[p]) ch[p][c]=nq;
        }
    }
    for (;np;np=fa[np]) 
     if (nxt[np]!=now) {
         size[np]++;
         nxt[np]=now;
     }
     else break;
}
int main()
{
    scanf("%d%d",&n,&m);
    root=++cnt;
    for(int i=1;i<=n;i++) {
        scanf("%s",s+1);
        last=root;
        len=strlen(s+1);
        now=i;
        for (int j=1;j<=len;j++) 
         extend(j);
    }
    for (int i=1;i<=m;i++) {
        scanf("%s",s+1);
        len=strlen(s+1);
        p=root;
        for (int j=1;j<=len;j++)  p=ch[p][s[j]-'a'];
        printf("%d\n",size[p]);
    }
}
View Code

 (對於后綴數組,在下還不是很敏感,多做點之后再補充一些上來)

 

 順便發兩張后綴自動機的圖

 

狀態 子串 endpos
S 空串 {0,1,2,3,4,5,6}
1 a {1,2,5}
2 aa {2}
3 aab {3}
4 aabb,abb,bb {4}
5 b {3,4,6}
6 aabba,abba,bba,ba {5}
7 aabbab,abbab,bbab,bab {6}
8 ab {3,6}
9 aabbabd,abbabd,bbabd,babd,abd,bd,d {7}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM