回文とは、始めから読んでも終わりから読んでも同じ文字列のことです。回文には abaのようなものとabbaのようなものがあります。いまは奇数長の回文のみを考えます。

文字列 s のi番目の文字を中心とする回文が存在し、その長さをnとしたとき、(n + 1) / 2をi番目の文字を中心とする最長回文半径と定義します。

Manacherのアルゴリズムを用いることで文字列の各位置を中心とする最長回文半径を線形時間で求められるようになります。まずはManacherのアルゴリズムを使わない愚直な方法についてみていきます。

Manacherのアルゴリズムを使わない場合

こっちのほうがスマートでよさそう。

このアルゴリズムの時間計算量は O(文字列の長さ^2) となります。3000文字程度であればこれでも問題ありませんが、長い文字列になると時間がかかります。

ところで回文には前後を反転させても同じになる性質があります。では回文の最長回文半径がどうなっているでしょうか?たとえば回文 ‘aaabbbaaa’ の最長回文半径は〈1, 2, 1, 1, 5, 1, 1, 2, 1〉です。回文だと最長回文半径そのものも対称性を持っていることがわかります。この対称性を利用できないでしょうか?

Manacherのアルゴリズムで最長回文半径を求める

最長回文半径 p[i] を求める単純なアルゴリズムは、以下のようなものでした。

各最長回文半径を求める際に、今まで見た最長回文の右端の中で最も右にあるものをフロンティア、右端が最も右にある最長回文をフロンティアの最長回文と定義します。するとManacher のアルゴリズムは、

1. フロンティアの回文が i 番目の文字を覆っている場合はフロンティアの最長回文をもとに、回文の性質を使って暫定的な最長回文半径を求める
2. 今見ている最長回文の右端がフロンティアの位置に一致している場合のみ、最長回文半径を再計算してフロンティアを更新する

と表現することができます。このようなアルゴリズムで各文字を中心とする最長回文半径を求めることができるのです。

偶数長の回文の最長回文半径

ここまでは奇数長の回文のみを考えてきました。しかし実際には偶数長の回文も存在します。では偶数長の回文の最長回文半径はどのようにして考えればよいでしょうか?

偶数長の回文とは文字と文字の間に中心がある回文です。そこで元の文字列の最初と最後および各文字間に適当な記号 ($ など) を挟んで奇数長の回文の最長回文半径を求める処理をおこないます。そして求められた回文半径を2で割る(端数切り捨て)と最長回文半径を求めることができます。

文字列の各文字から始まる最長回文の長さを求める

各文字または各文字とその次の文字のあいだを中心とする最長回文半径から、回文の長さを求めることができます。また回文の長さを求めることができれば長さと最長回文半径の中心の文字の位置から回文の開始点を求めることができます。