web-dev-qa-db-ja.com

O(n)の回文部分文字列を数える

文字列(英語の文字のみを想定)Sの長さnを指定すると、次のアルゴリズムで回文部分文字列の数をカウントできます。

_for i = 0 to |S| do
    p1 = number of palindromes centered in i (odd length)
    p2 = number of palindromes centered in i and i+1 (even length)

    add p1 + p2 to total number of palindromic substrings of S
_

上記のコードはO(n^2)です。

この問題をO(n)で解決するアルゴリズムに興味があります。複数の人がそうだと言っているのを聞いたので、確かに存在します。問題は、nの上限が_1 000 000_であるローカルのオンライン裁判官サイトにありますが、私はアルゴリズムを見たことがなく、アルゴリズムを思い付くことができないようです。

更新:

私が持っている一般的な考え方は、_len[i] = length of the longest palindrome centered at the character 2i + 1_と同様の長さの偶数長の回文の配列を計算することです。優れた簿記があれば、これを各文字のO(1)で計算できるはずです。これにより、多くの回文を一度に数えることができます。しかし、これを正確に計算する方法にこだわっています。

O(n)O(n log n)の追加メモリを使用するソリューションを受け入れます。これなしではこれは不可能だと思います。

良いアイデアや参考文献はありがたいです。

31
IVlad

次のサイトは、O(n)時間で最長の回文部分文字列を計算するためのアルゴリズムを示しています。可能な限りすべての中心で最長の回文部分文字列を計算し、最大値をとることによって計算します。目的に合わせて簡単に変更できるはずです。

http://www.akalin.cx/2007/11/28/finding-the-longest-palindromic-substring-in-linear-time/

編集:最初のリンクはよく見ると少し不安定に見えるので、別のリンクを次に示します。

http://zhuhcheng.spaces.live.com/Blog/cns!DE38E96268C49F28!311.entry?wa=wsignin1.0&sa=707413829

7
Paul Accisano

「通常の」文字列の場合は、各文字を回文の潜在的な「中心」と見なして、周囲の文字が実際に1つを構築しているかどうかを確認する方が効率的です。

# check odd palindromes
for center in range(len(ls)):
   # check how many characters to the left and right of |center|
   # build a palindrome
   maxoffs = min(center, len(ls)-center-1)
   offs = 0
   while offs <= maxoffs and ls[center-offs] == ls[center+offs]:
      offs += 1
   offs -= 1
   print ls[center-offs : center+offs+1]                                    

# check for even palindromes
for center in range(len(ls)-1):
   maxoffs = min(center, len(ls)-center-2)
   offs = 0
   while offs <= maxoffs and ls[center-offs] == ls[center+offs+1]:
      offs += 1
   offs -= 1
   if offs >= 0:
      print ls[center-offs : center+offs+2]

通常の文字列の場合、これは約O(n)になりますが、最悪の場合、たとえば、文字列が1文字だけで何度も繰り返されている場合でも、O(n)がかかります2)時間。

1
sth

文字列S="aaabb"を考えます。

文字列の両端と2つの連続する文字の間に文字'$'を追加して、文字列をS="$a$a$a$b$b$"に変更し、この文字列に Manacher's algorithm を適用しますS

新しい文字列Sの長さは2n + 1で、O(n)と同じO(2n + 1)のランタイムを提供します。

index :  1 2 3 4 5 6 7 8 9 10 11
A     :  1 3 5 7 5 3 1 3 5  3  1
S     :  $ a $ a $ a $ b $  b  $

配列AはManacherのアルゴリズムの結果です。

ここで、インデックスのA[i]/4の合計、ここで'$'、それ以外の場合は1 <= i <= nの他のすべての文字の(A[i]+1)/4が答えです。

ここで、$は、偶数長のパリドロームのサブストリングの中心として機能し、奇数長は通常どおり計算できます。この場合の答えは次のとおりです。

0 + 1 + 1 + 2 + 1 + 1 + 0 + 1 + 1 + 1 + 0 = 9(a、a、aaa、a、b、b、aa、aa、bb)。

1
Aakash Prakash