web-dev-qa-db-ja.com

サフィックスツリーを使用した文字列内の最長パリンドローム

弦の中で最も長い回文を見つけようとしていました。ブルートフォースソリューションにはO(n ^ 3)時間かかります。サフィックスツリーを使用した線形時間アルゴリズムがあることを読みました。私は接尾辞の木に精通しており、それらを快適に構築しています。構築された接尾辞ツリーを使用して、最長の回文を見つける方法.

46
shreyasva

この方法で進める必要があると思います。

みましょうy1y2 ... yn あなたの文字列である(yi 文字です)。

Sの一般化されたサフィックスツリーを作成しますf = y1y2 ... yn$およびSr = ynyn-1 ... y1(文字を逆にし、Sに別の終了文字を選択しますf($)およびSr(#))...ここでSf"String、Forward"およびSr"String、Reverse"を表します。

すべてのサフィックスについてi in Sf、接尾辞n-i + 1 in Sr

最下位の共通の祖先はこれら2つの接尾辞の最長の共通の接頭辞を表すため、ルートからこの最下位の共通の祖先まで実行されるのは回文です。それを思い出します:

(1)suffixprefixsubstringです。

(2)palindromeは、その逆と同じ文字列です。

(3)したがって、文字列内に含まれる最長の回文は、この文字列の最も長い共通部分文字列であり、その逆です。

(4)したがって、文字列内に含まれる最長の回文は、文字列とその逆の間のsuffixesのすべてのペアの中で最も長い共通のprefixです。これがここでやっていることです。

[〜#〜] example [〜#〜]

言葉を取りましょうbanana

Sf = banana $

Sr = ananab#

以下はSの一般化されたサフィックスツリーですfおよびSr、各パスの末尾の数字は、対応するサフィックスのインデックスです。 Blue_4の親の3つのブランチすべてに共通のaは、入力エッジにnの横にあるはずです。

enter image description here

ツリーの最も低い内部ノードは、この文字列の最も長い共通部分文字列であり、その逆です。したがって、ツリー内のすべての内部ノードを見ると、最も長い回文が見つかります。

最も長い回文は、Green_0とBlue_1の間(つまり、bananaanana)で見つかり、ananaです。


[〜#〜] edit [〜#〜]

私はたった今見つけました このペーパー それはこの質問に答えます。

25
Ricky Bobby

線形解はこの方法で見つけることができます::

要件:

(1).O(N)またはO(NlogN) time。でサフィックス配列を作成する方法を知っている必要があります。

(2).標準LCPアレイを見つける方法を知っている必要があります。隣接する接尾辞iとi-1の間のLCP

すなわち。 (i> 0)の場合、LCP [i] = LCP(ソート済み配列のサフィックスi、ソート済み配列のサフィックスi-1)。

[〜#〜] s [〜#〜]を元の文字列とし、S 'を元の文字列の逆にします。例としてS = "banana"を考えてみましょう。次に、その逆ストリングS '= ananab。

ステップ1S +#+ S 'を連結して文字列Strを取得します。#は元の文字列に存在しないアルファベットです。

    Concatenated String Str=S+#+S'
    Str="banana#ananab"

ステップ2:次に、文字列Strのサフィックス配列を作成します。

この例では、接尾辞配列は次のとおりです。

Suffix Number   Index   Sorted Suffix
0               6       #ananab
1               5       a#ananab
2               11      ab
3               3       ana#ananab
4               9       anab
5               1       anana#ananab
6               7       ananab
7               12      b
8               0       banana#ananab
9               4       na#ananab
10              10      nab
11              2       nana#ananab
12              8       nanab

サフィックス配列は、辞書式順序で文字列のサフィックスの開始位置を与える整数の配列であることに注意してください。したがって、開始位置のインデックスを保持する配列はサフィックス配列です。

つまり、SuffixArray [] = {6,5,11,3,9,1,7,12,0,4,10,2,8};

ステップ3:サフィックスアレイを構築できたので、隣接するサフィックスの間にLongest Common Prefixesを見つけます

LCP between #ananab        a#ananab          is :=0
LCP between a#ananab       ab                is :=1
LCP between ab             ana#ananab        is :=1
LCP between ana#ananab     anab              is :=3
LCP between anab           anana#ananab      is :=3
LCP between anana#ananab   ananab            is :=5
LCP between ananab         b                 is :=0
LCP between b              banana#ananab     is :=1
LCP between banana#ananab  na#ananab         is :=0
LCP between na#ananab      nab               is :=2
LCP between nab            nana#ananab       is :=2
LCP between nana#ananab nanab                is :=4

したがって、LCP配列LCP = {0,0,1,1,3,3,5,0,1,0,2,2,4}。

ここで、LCP [i] =サフィックスiとサフィックス(i-1)の間の最長共通プレフィックスの長さ。 (i> 0の場合)

ステップ4:

これで、LCPアレイを構築しました。次のロジックを使用します。

    Let the length of the Longest Palindrome ,longestlength:=0 (Initially)
    Let Position:=0.
    for(int i=1;i<Len;++i)
    {
        //Note that Len=Length of Original String +"#"+ Reverse String
        if((LCP[i]>longestlength))
        {
            //Note Actual Len=Length of original Input string .
            if((suffixArray[i-1]<actuallen && suffixArray[i]>actuallen)||(suffixArray[i]<actuallen && suffixArray[i-1]>actuallen))
            {
                 //print :Calculating Longest Prefixes b/w suffixArray[i-1] AND  suffixArray[i]


                longestlength=LCP[i];
              //print The Longest Prefix b/w them  is ..
              //print The Length is :longestlength:=LCP[i];
                Position=suffixArray[i];
            }
        }
    }
    So the length of Longest Palindrome :=longestlength;
    and the longest palindrome is:=Str[position,position+longestlength-1];

実行例::

    actuallen=Length of banana:=6
    Len=Length of "banana#ananab" :=13.

Calculating Longest Prefixes b/w a#ananab AND  ab
The Longest Prefix b/w them  is :a 
The Length is :longestlength:= 1 
Position:= 11




Calculating Longest Prefixes b/w ana#ananab AND  anab
The Longest Prefix b/w them  is :ana
The Length is :longestlength:= 3 
Position:=9



Calculating Longest Prefixes b/w anana#ananab AND  ananab
The Longest Prefix b/w them  is :anana
The Length is :longestlength:= 5 
Position:= 7

So Answer =5.
And the Longest Palindrome is :=Str[7,7+5-1]=anana

メモするだけ::

ステップ4のif条件は、基本的に、各繰り返し(i)で、接尾辞s1(i)およびs2(i-1)を取得する場合、 #およびs2には# "OR" s2には#が含まれ、s1には# "。が含まれてはならない

 |(1:BANANA#ANANAB)|leaf
tree:|
     |     |      |      |(7:#ANANAB)|leaf
     |     |      |(5:NA)|
     |     |      |      |(13:B)|leaf
     |     |(3:NA)|
     |     |      |(7:#ANANAB)|leaf
     |     |      |
     |     |      |(13:B)|leaf
     |(2:A)|
     |     |(7:#ANANAB)|leaf
     |     |
     |     |(13:B)|leaf
     |
     |      |      |(7:#ANANAB)|leaf
     |      |(5:NA)|
     |      |      |(13:B)|leaf
     |(3:NA)|
     |      |(7:#ANANAB)|leaf
     |      |
     |      |(13:B)|leaf
     |
     |(7:#ANANAB)|leaf
29
ritesh_NITW

数年遅れて...

sが元の文字列であり、rsであると仮定します。また、STを使用して接尾辞ツリーsを完全に構築したと仮定します。

次のステップは、rのすべてのサフィックスをSTに対してチェックすることです。 rの新しいサフィックスごとに、ツリー内の既存のサフィックス(つまり、kのサフィックスの1つ)と正常に一致した最初のs文字のカウントを維持します。

例として、rのサフィックス"RAT"に一致し、sには"RA"で始まるサフィックスが含まれていましたが、一致したものはなかったとします「RAT」kは、最終文字"T"の希望を最終的に放棄しなければならなかったときに2になります。 rのサフィックスの最初の2文字をsのサフィックスの最初の2文字と一致させました。 nに到達したこのノードを呼び出します。

さて、パリンドロームを見つけたとき、どうやって知るのでしょうか? nの下のすべてのリーフノードをチェックすることにより

従来のサフィックスツリーでは、各サフィックスの開始インデックスは、そのサフィックスブランチのリーフノードに格納されます。上記の例では、sには"RA"で始まるサフィックスの束が含まれている可能性があり、各サフィックスはnのリーフノードの子孫にあるインデックスの1つで始まります。

これらのインデックスを使用しましょう。

kの部分文字列のR文字をkST文字と照合するとどうなりますか?まあ、それは単に文字列が逆になったことを発見したということです。しかし、Rでサブストリングが始まる場所が、Sで一致したサブストリングにkを加えたものと等しい場合、どういう意味ですか?はい、それはs[i] through s[i+k]s[i+k] through s[i]と同じことを意味することを意味します!それで、定義でサイズkの回文を見つけました。

さて、あなたがしなければならないことは、これまでに発見された最長の回文のタブを保持し、関数の最後にそれを返すことです。

5
sgarza62

Skiena - The Algorithm Design Manualからの簡単で短い説明

Sの最長パリンドロームを検索[サフィックスツリーを使用] – A パリンドロームは、madam。文字列Sで最長のパリンドロームを見つけるには、Sのすべての接尾辞とSの反転を含む単一の接尾辞ツリーを構築し、各葉を開始位置で識別します。回文は、同じ位置から前方および後方の子を持つこのツリー内の任意のノードによって定義されます。

1
Zsolt Safrany