※ 大きい画面での閲覧を推奨します。
問題の説明
文字列 S の空文字列を除く連続部分文字列のうち、回文(前から読んでも後ろから読んでも等しい文字列)である文字列の個数を数えてください。ただし、複数の部分文字列が文字列として一致する場合もその部分文字列を取り出してきた場所が違う場合には区別します。(問題文はこれをより厳密に表現しています。)
例
S が abcba のとき、abcbaabcbaabcbaabcbaabcbaabcbaabcba の 7 個の連続部分文字列が回文となります。
S が aaaaa のとき、aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa の 15 個の連続部分文字列が回文となります。
解説
回文である連続部分文字列のことを回文連続部分文字列ということにします。
Manacher's algorithm を用いると、文字列の各位置を中心とする最長の回文連続部分文字列の長さを線形時間で列挙することができます。
具体的には、文字列 abbba に対して
というような配列を時間計算量 O(N) で作成することができます。ここで、N は文字列の長さを表します。
長さ 5 の回文には同じ位置を中心とする長さ 3, 1 の回文が含まれます (例えば abcba には bcb と c が含まれる)。
長さ 6 の回文には同じ位置を中心とする長さ 4, 2 の回文が含まれます (例えば abccba には bccb と cc が含まれる)。
同様に、文字列のある位置を中心とする最長の回文連続部分文字列の長さが n であるとき、その位置を中心とする回文連続部分文字列の個数は ⌈2n⌉ (回文の「半径」と呼ばれる値)となります。よって、Manacher's algorithm を用いて最長の回文連続部分文字列の長さを格納した配列を求めた後に各要素を 2 で割って切り上げた値の総和を求めることによって時間計算量 O(N) でこの問題を解くことができます。(元から回文の半径を求める実装をしている場合は配列の要素の総和を取るだけでよいです。)
解答例(Python 3)
解答例(C++)
この問題は完全に知識問題(Manacher's algorithm を知っていれば解けるし知らなければ解けない)だと思って出題したのですが、皆さんの解答を見ると他の方法でも解かれているようです。興味のある方は他の解法も考えてみてください。