接尾辞配列とその応用

ここでは簡単に乗算アルゴリズム\((O(nlogn)) \) を求める接尾辞配列


定義

定義\(S \)の長さの\(N- \)文字列、サフィックスの\(SUF [I] \)手段\(S [I \ N-SIM] \)

\(S \)すべてのサフィックスのは、辞書順にソート:

  • \(RK [I](ランク [i])と\) 手段\(SUF [I] \)のランキング。
  • \(SA [i]が\)セクションを指す\(I \)接尾辞である\(SUF [SA [I] \)
  • \([i]の高さ\)部分を指す(I \)\と第\(I-1 \)最長の共通接頭名サフィックス\((LCP)\)

アルゴリズム論

\(SA \)アレイ評価方法

入手し\(SA \)取得する配列(RK \)\ ;

まず、我々は簡単に考えることができ、迅速放電文字比較アルゴリズムの時間によって文字に基づいている場合があり、アルゴリズム(O(N ^ 2logn)\)\ ;

我々はまたして、最適化されたと考えることができます(ハッシュ+ \)\の代わりにすることにより、文字の比較の半分は、この時間は、\(O(nlog ^ 2n個)\)

その他のアイデア:

接尾辞としてそれぞれが形成することができる\(ASCII \)シリーズコードにより構成される基数ソート時間があるように、達成する(O(N ^ 2)\ \) ;及び

最適化するための方法を考えます:

ソートする文字列であるため、\(S \)サフィックス以上、それは同じ部分です。

だから我々はこのようなアルゴリズムを導入できること:

各キーの最初の接尾辞として彼らにランクを与えるために、各順接尾辞の最初の文字については、1。

各サフィックスの最初の二つの選別(1 + 1)番目の文字、(2. \(SUF [I] \)最初の2つの文字の\(S [i]は、S [I + 1] \) 、全体の一種として見すでにここのランキングのサブ文字列の長さへの第一歩は、自分が)マージソート後のランキングに相当し、その後、各サフィックスを与えるために、それらをランク付けするため、最初のキーワード;

各接尾辞(1 + 2)文字の最初の三つのソート順3.、それらは各サフィックスの最初のキーワードランキングとして得られます。

4.順次、最初の5つのサフィックス(1 + 4)文字のそれぞれに対して順序付け

......

すべてのランキングは、互いに異なるようになるまで。

絵を見て、より良い理解は、ノンストップのプロセスを再ソートマージソートである。このような問題は、合併で解決し、各サフィックスのされているすべての時間は、すべての文字を含むように漏れないことはできません最後のステップであることを保証することができます効果は、暴力の基数ソートの一種に相当します。

乗算の組み合わせた長さが実装されているため、また、それが最もBE \(log_2n \) 二次区分は、それぞれの中央値に使用されるソート\(2 \)ソート基数のを作ることができるアルゴリズムの時間複雑さはに維持される(O \ (nlogn)\)レベル。

コードのこの部分

#include<bits/stdc++.h>
#define ll long long 
#define mp make_pair
using namespace std;

const int N=100005;
char c[N];
int n,m=127;//m是值域的初始大小  
int tub[N];//桶 
int fk[N],psk[N];//注意!!!fk(first key)是某个位置的一关键字 
//而psk(pos of second key)是 排名为i的二关键字所在的位置 
int sa[N],height[N],rk[N];
//ll ans;

inline int read()
{
    int x=0,fl=1;char st=getchar();
    while(st<'0'||st>'9'){ if(st=='-')fl=-1; st=getchar();}
    while(st>='0'&&st<='9') x=x*10+st-'0',st=getchar();
    return x*fl;
}

inline void rsort()//基数排序 
{
    for(int i=1;i<=m;i++) tub[i]=0;//清空桶 
    for(int i=1;i<=n;i++) ++tub[fk[i]];//将第一关键字加入桶中 
    for(int i=1;i<=m;i++) tub[i]+=tub[i-1];//求桶的前缀和,得到一关键字的排名 
    for(int i=n;i>=1;i--) sa[tub[fk[psk[i]]]--]=psk[i];//按照从后到前的顺序,依据二关键字的排名得到目前的sa 
}

inline void SA()
{
    for(int i=1;i<=n;i++) fk[i]=c[i],psk[i]=i; //初始 
    rsort();//第一次排序 
    for(int k=1;k<=n;k<<=1)//合并的长度 
    {
        int cnt=0;
        for(int i=n-k+1;i<=n;i++) psk[++cnt]=i;//这一部分位置是没有能与其合并的 ,其二关键字应是最小的 
        for(int i=1;i<=n;i++)
            if(sa[i]>k) psk[++cnt]=sa[i]-k; //这里的sa[i]会被某个位置合并,这个位置是sa[i]-k
        //这一部分的二关键字是按照顺序加入的 
        rsort();
        swap(fk,psk);//将fp放到psk转存,方便得到下一次的一关键字 
        fk[sa[1]]=1;
        cnt=1;
        for(int i=2;i<=n;i++) //如果两个位置的一关键字和二关键字都相同,则排名不同,需要+1  
            fk[sa[i]]=(psk[sa[i]]==psk[sa[i-1]]&&psk[sa[i]+k]==psk[sa[i-1]+k])?cnt:++cnt;
        if(cnt==n) break;//如果已经所有排名不同了就可以退出 
        m=cnt;//更新值域 
    }
}

int main()
{
    n=read();
    scanf("%s",c+1);
    SA();
//  get_height();
//  ans=(ll) n*(n+1)/2;
//  for(int i=1;i<=n;i++)
//      ans-=height[rk[i]];
//  printf("%lld",ans);
    return 0;
}

\(高\)アレイを探しています

これは、プロパティに主に依存しています:

\(身長[RK [I]≥高さ[RK [I-1] - 1 \)

誘導性の証明:

ランク仮定する(SUF [I-1] \ \) 文字列の前にある(SUF [K] \)\その最長の共通のプレフィックス長\(身長[RK [-I 1] \)

\(SUF [I] \)より(SUF [I-1] \ \) 、それがなければ文字、使用可能の\(SUF [I] \)\(SUF [K + 1] \) の最長の共通のプレフィックスである\(身長[RK [-I 1] - 1 \。)

しかし\(SUF [I] \)\(SUF [K + 1] \) (すなわち、一緒に必ずしもソートにおける\(身長[RK [I] ]が\) 必ずしもその最長の共通のプレフィックスではありません) 、

でも一緒にいない場合でも、ソートされた接尾辞\(SUF [K + 1] \) と(SUF [I] \)\サフィックスは必ずしも間で満たされ、そして(SUF [I] \)\最高共通のプレフィックスの長さは、少なくともある\(身長[RK [-I 1]] - 1 \。)

その後がなければならない(高さ[RK [I]]≥高さ[RK [-I 1]。] - 1 \。)\ ;

これが達成\(O(N)\)コード

inline void get_height()
{
    int k=0;
    for(int i=1;i<=n;++i) rk[sa[i]]=i;
    for(int i=1;i<=n;++i)  //i是位置不是排名 
    {
        if(k)--k;//-1
        int j=sa[rk[i]-1];//找到排名前一个的位置 
        while(j+k<=n&&i+k<=n&&c[i+k]==c[j+k])++k;//继续匹配 
        height[rk[i]]=k;
    }
}

一部のアプリケーション

任意の2つのサフィックスを掲載(LCP \)\

\(SUF [I] \)\(SUF [J] \)(\ (RK [I] <RK [J] \))の\(LCP \)長さ\(\分\ limits_ {K = RK [I] + +1} ^ {RK [J]}高[K] \)

これは、最小間隔の問題となり、

部分文字列とは本質的に異なります

文字列\(S \)すべてのサブストリングの数である(N *(N + 1)/ 2 \)\

文字列\(S \)すべてのサフィックスが来るためにすべてのサブ文字列の接頭辞であってもよく、

同じ有している([I] \和高さ\ \) 番目。

だから、次のとおりです。

\ [ANS = \ dfrac {N \倍(N + 1)} {2} - \和高さ[I] \]

二つの文字列の最長共通部分列

\(DP \)の種類のうちの時間。

単純なアプローチは以下のとおりです。

2つの文字列が1つの文字列に結合され、そして新しい文字列接尾辞配列を得、離間文字を中央に表示されない(例えば、%)。

最大取得\(身長[I] \) 満足\(SA [I] \)\(SA [I-1] \) 内部で異なるストリング。

このアプローチは、最長共通部分列複数の文字列に拡張することができますが、時間が増加します。

より多く表示される\(K \)最長サブ

長さを取得する\(K-1 \)シーケンス\(高さ[] \)最小の最大値;

おすすめ

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