弦の中で最も長い回文を見つけようとしていました。ブルートフォースソリューションにはO(n ^ 3)時間かかります。サフィックスツリーを使用した線形時間アルゴリズムがあることを読みました。私は接尾辞の木に精通しており、それらを快適に構築しています。構築された接尾辞ツリーを使用して、最長の回文を見つける方法.
この方法で進める必要があると思います。
みましょう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)suffixのprefixはsubstringです。
(2)palindromeは、その逆と同じ文字列です。
(3)したがって、文字列内に含まれる最長の回文は、この文字列の最も長い共通部分文字列であり、その逆です。
(4)したがって、文字列内に含まれる最長の回文は、文字列とその逆の間のsuffixesのすべてのペアの中で最も長い共通のprefixです。これがここでやっていることです。
[〜#〜] example [〜#〜]
言葉を取りましょうbanana。
Sf = banana $
Sr = ananab#
以下はSの一般化されたサフィックスツリーですfおよびSr、各パスの末尾の数字は、対応するサフィックスのインデックスです。 Blue_4の親の3つのブランチすべてに共通のaは、入力エッジにnの横にあるはずです。
ツリーの最も低い内部ノードは、この文字列の最も長い共通部分文字列であり、その逆です。したがって、ツリー内のすべての内部ノードを見ると、最も長い回文が見つかります。
最も長い回文は、Green_0とBlue_1の間(つまり、bananaとanana)で見つかり、ananaです。
[〜#〜] edit [〜#〜]
私はたった今見つけました このペーパー それはこの質問に答えます。
線形解はこの方法で見つけることができます::
要件:
(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。
ステップ1:S +#+ 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
数年遅れて...
s
が元の文字列であり、r
がs
であると仮定します。また、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
文字をk
のST
文字と照合するとどうなりますか?まあ、それは単に文字列が逆になったことを発見したということです。しかし、R
でサブストリングが始まる場所が、S
で一致したサブストリングにk
を加えたものと等しい場合、どういう意味ですか?はい、それはs[i] through s[i+k]
がs[i+k] through s[i]
と同じことを意味することを意味します!それで、定義でサイズk
の回文を見つけました。
さて、あなたがしなければならないことは、これまでに発見された最長の回文のタブを保持し、関数の最後にそれを返すことです。
Skiena - The Algorithm Design Manual
からの簡単で短い説明
Sの最長パリンドロームを検索[サフィックスツリーを使用] – A パリンドロームは、madam。文字列Sで最長のパリンドロームを見つけるには、Sのすべての接尾辞とSの反転を含む単一の接尾辞ツリーを構築し、各葉を開始位置で識別します。回文は、同じ位置から前方および後方の子を持つこのツリー内の任意のノードによって定義されます。