一部のアプリケーション接尾辞オートマタ

サフィックスオートマトンを学習していない、あなたが行くことができるためにここを見て
 
 
 

文字列が変化するので、ここではいくつかある\(SAM \)理解の数を増加させるための簡単なアプリケーションでは、。
 
 
 

アプリケーションサフィックスオートマトン

 
 
 

異なる性質の部分文字列の数

我々は持っている\(SA \)での実践列\(SAM \)はまた、上で同様のことを行います。

その検索\(SAM \)サブにすることは、私たちだけです統計繰り返されていません\(SAMを\)上のすべてのサブ文字列。

\ [ANS = \のMaxLen和[I] -minlen [I] + 1 \]

 
 
 

出現をサブストリング統計

入手し\(SAM \)それぞれの各文字列の出現回数。

、それぞれの状態を求めている実際には\(endposの\)コレクションのサイズを、どのようにそれを見つけるには?

まず、新しい状態が追加されるたび(endposの\)\少なくとも一つあります。

さらに、それぞれのための状態\(リンク\)のエッジには、この寄与は、ステータスを生成します。

\(NUM [I] + = \ sum_ {リンク[J] = I} [J]か\)

これがかもしれ\(リンク\)で構成\(DAG \)トポロジ上で実行します。

 
 
 

二つの最長共通文字列

\(例:。\) SP1811

\(SA \)いなかった、あなたが最初の最初の文字列の上に構築することができます\(SAM \)はすべてのサブストリングになりますので、\(SAMは\)に反射し、我々は、この第2の文字列を考慮することができます\(SAM \)が一致。

 
 
 

アルゴリズムのプロセス:

現在の状態\(P \) 最初\(1 \) )、一致する文字\(S [I] \)の長さマッチ\(LEN \)

  1. もし\(トランス[P] [S [I] = 0 \!) サブストリングこの手段は、\(LEN ++ \) 次の状態に移行します。
  2. それ以外の場合は、一緒に\(リンク\)楽しみにして:
    • 我々は状態を見つけることができる場合は、\(P \)を、満たす\(トランス[P] [S [I]]!= 0 \)とすることができる(LEN = maxlenを[P]さんが+1 \)\に進み、(\トランス[P] [S [I]] \)
    • そうでなければ\(LEN = 0、P-1 = \)
  3. 更新は答えます。

時間計算量は\(O(N)\)、\ (N- \)二つの文字列の長さの合計です。

 
 

コードの一部

void search(int n)
{
    int p=1;
    for(int i=1;i<=n;i++)
    {
        int c=s[i]-'a';
        if(trans[p][c]) len++,p=trans[p][c];
        else
        {
            for(;p&&!trans[p][c];p=link[p]);
            if(p) len=maxlen[p]+1,p=trans[p][c];
            else len=0,p=1;
        }
        ans=max(ans,len);
    }
}

 
 
 

文字列の複数の最長共通部分

\(例:。\) SP1812

仮定\(K \)の長さの\(N- \)文字列を、

 
 

それは2つの方法について説明します

 

1 O(NK ^ 2)

統計的には、リストに追加するために、同様の発生をサブストリング\(I \)の各状態の文字列\(I \)符号でマークされた寸法、即ち\(NUM [X] [I ] = 1 \ ;

そして、各状態のトポロジや深い検索の統計情報各文字列の中に出現数。

状態がある場合は、\(X- \) それぞれが(、各文字列に登場してきていること)、あなたは答えを更新するためにそれを使用することができ、その値を持っています。

時間複雑である\(O(NK ^ 2)\) それのいくつかの例の後に、それは十分に理解されています。

コード際に深い検索

inline void dfs(int x)
{
    for(int i=head[x];i;i=a[i].nxt)
    {
        dfs(a[i].to);
        for(int j=0;j<t;j++)//t是个数
            T.num[x][j]+=T.num[a[i].to][j];
    }
    bool fl=0;
    for(int j=0;j<t;j++)
        if(!T.num[x][j])
        {
            fl=1;
            break;
        }
        
    if(!fl) ans=max(ans,T.ml[x]);
}

 
 
 

2 O(NK)

 
 
 

別の

二つの文字列から拡大しようとしている場合。

最初の文字列の上に構築を検討\(SAM \)の各文字列の残りの部分はなっていた、(SAM \)\それを一致させるためにアップ。

場合各一致、現在の一致、それぞれうまくマッチした状態の最長の長さを与えます。

好ましいケースによれば、最後は最長各マッチの最小値を見つけるために、すべてのマッチが成功したマッチが最長共通特定の状態の長さです。

 

状態があればしかし、問題があるでしょう(X \)\そのの成功一致\(リンク[X] \)にも存在している必要があります\(X \)試合、我々はあまりにも到着しなかった\(リンク[X] \)を、我々は更新する必要がありますので、値のこの部分は、存在することはできません\(リンク[X-] \)を

 

しかし、によるもの(リンク[X] \)\自分の限られた長さ、我々は入れなければなら\(MAXN [リンク[X] ] = MAX(MAXN [リンク[X]]、分(MAXN [x]は、maxlenを[リンク[Xを]]))\)

\(MAXN [X] \)の状態である)(X \ \現在の最長一致長のマッチングが成功した場合)

 

このプロセスは完全に更新されることを保証するために、我々は、更新することであるトポロジーによって順序を更新する必要が\(X \)更新には\(リンク[X-] \)

我々が見つかりました- (> maxlenを[リンク[X-]] \ [X] maxlenを)\そうに従い、(MAXLEN \)\ ;それにランキング更新、トポロジカル整列を保存することができます

 

この時間は、\(O(NK)\)

コードの場合

//这都在SAM结构体中
void topu()
{
    for(int i=1;i<=sz;i++) tub[ml[i]]++;
    for(int i=1;i<=sz;i++) tub[i]+=tub[i-1];
    for(int i=sz;i>=1;i--) b[tub[ml[i]]--]=i;
}
//类似于后缀数组,用一次基数排序得到拓扑序
void work()
{
    int l=0,p=1;
    for(int i=1;i<=n;i++)
    {
        int x=c[i]-'a';
        if(trans[p][x]) l++,p=trans[p][x];
        else
        {
            for(;p&&!trans[p][x];p=link[p]);
            if(p) l=ml[p]+1,p=trans[p][x];
            else l=0,p=1;
        }
        mas[p]=max(mas[p],l);
    }
    for(int i=sz;i>=1;i--)
    {
        int x=b[i],li=link[x];
        mas[li]=max(mas[li],min(mas[x],ml[li]));
        mis[x]=min(mis[x],mas[x]);
        mas[x]=0;
    }
}

 
 
 

辞書順でのk番目の最大の部分文字列

\(例:。\) P3675

2つの場合があります。

マージは基本的に同じ順序ではない場合:

思想に基づいており、\(SAM \)サブの対応する一つのパス。

最初の要求\(K \)大サブストリングは、第計算することである\(K \)大きいパスを、

我々は、我々は、バイナリ検索ツリーとして、この問題を置くことができ、より行うにはどのくらいの各状態の接続経路を知ることができれば、二分探索木のアプローチと同様に、

 

我々はまた、問題を考慮する必要があります。

各状態は、複数有し(endposの\)\状態とコピーのかなりの数であり、;

我々はまだ覚えている(| endposの[I] | \ \) に\([I] NUM \)を

最初の\(I \)パスの状態の総数は、この状態を貫通サブストリングの数は、と呼ばれる([I] SUM \)\

、状態自体があるので\(NUM [I] \)サブストリングは、とは、先に後の得られたサブストリングと一致し続けます。

 

あなたは得ることができます

\ [和[I] = NUM​​ [I] + \ sum_ {トランス - [I] [J] = X}和[J] \]

 

別の状況:

本質的には同じ組み合わせた文字列。

実際には、(開始状態を除く)各状態\(NUMは\)に設定されている\(1 \)

 

\(コード\)

#include<bits/stdc++.h>
#define ll long long 
#define mp make_pair
using namespace std;
const int N=500010;
char c[N];
int n,ty,k;

struct SAM
{
    int sz,last;
    int tub[N<<1],b[N<<1];
    int num[N<<1],sum[N<<1];
    int link[N<<1],trans[N<<1][26],ml[N<<1];
    SAM(){sz=last=1;}
    
    void add(int id)
    {
        int x=++sz,p;
        ml[x]=ml[last]+1;
        num[x]=1;
        for(p=last;p&&!trans[p][id];p=link[p]) trans[p][id]=x;
        if(!p) link[x]=1;
        else
        {
            int q=trans[p][id];
            if(ml[q]==ml[p]+1) link[x]=q;
            else
            {
                int y=++sz;
                ml[y]=ml[p]+1;
                memcpy(trans[y],trans[q],sizeof trans[y]);
                link[y]=link[q];
                for(;p&&trans[p][id]==q;p=link[p]) trans[p][id]=y;
                link[x]=link[q]=y;
            }
        } 
        last=x;
    }
    
    void topu()
    {
        for(int i=1;i<=sz;i++) tub[ml[i]]++;
        for(int i=1;i<=sz;i++) tub[i]+=tub[i-1];
        for(int i=sz;i>=1;i--) b[tub[ml[i]]--]=i;
    }
    
    void getsz()
    {
        for(int i=sz;i>=1;i--)
        {
            int x=b[i];
            sum[x]+=num[x];
            num[link[x]]+=num[x];
        }
        if(ty==0) for(int i=1;i<=sz;i++) sum[i]=num[i]=1;
    }
    
    void getsum()
    {
        num[1]=sum[1]=0;
        for(int i=sz;i>=1;i--)
        {
            int x=b[i];
            for(int j=0;j<26;j++)
                if(trans[x][j]) sum[x]+=sum[trans[x][j]];
        }
    }
    
}T;

inline void sol(int x,int k)
{
    if(k<=T.num[x]) return ;
    k-=T.num[x];
    for(int i=0;i<26;i++)
    {
        int y=T.trans[x][i];
        if(!y) continue;
        if(k>T.sum[y])
        {
            k-=T.sum[y];
        }
        else
        {
            printf("%c",'a'+i);
            sol(y,k);
            break;
        }
    }
}

int main()
{
    scanf("%s",c+1);
    scanf("%d%d",&ty,&k);
    n=strlen(c+1);
    for(int i=1;i<=n;i++)
        T.add(c[i]-'a');
    T.topu();
    T.getsz();
    T.getsum();
    if(T.sum[1]<k) printf("-1");
    else sol(1,k);
    
    return 0;
}

おすすめ

転載: www.cnblogs.com/yudes/p/SAM.html