Python
 Computer >> コンピューター >  >> プログラミング >> Python

Pythonで解く「最長チャンク回文分解」― 両端からの貪欲法アルゴリズム

問題の概要

ある文字列 text が与えられたとき、次の条件をすべて満たす最大の k を求める問題です。

  • a[i](i = 1〜k)は空でない文字列である
  • 連結した a[1] + a[2] + ... + a[k] が、与えられた text と完全に一致する
  • すべての i について a[i] = a[k+1-i] が成り立つ(前後のチャンクが対称になっている)

たとえば、入力が "antaprezatepzapreanta" の場合、出力は 11 になります。これは次のように11個のチャンクへ分割できるためです。

(a)(nt)(a)(pre)(za)(tpe)(za)(pre)(a)(nt)(a)

解き方のアプローチ

この問題は、文字列の両端から同時に走査する貪欲法(グリーディ法)で効率的に解けます。左端から伸ばしていく部分文字列 temp1 と、右端から伸ばしていく部分文字列 temp2 が一致したタイミングで、そのペアを1組としてカウントしていきます。

アルゴリズムの手順

  1. start := 0end := 文字列長 - 1 と初期化します。
  2. temp1temp2 を空文字列で初期化します。
  3. ans を、文字列長が奇数なら 1、偶数なら 0 に設定します。
  4. start < end の間、以下を繰り返します。
    • temp1 := temp1 + text[start]
    • temp2 := text[end] + temp2
    • temp1temp2 が一致したら、両方を空文字列にリセットし、ans := ans + 2 とします。
    • start := start + 1end := end - 1
  5. 文字列長が偶数で、かつ temp1 または temp2 が空でない場合は、ans := ans + 1 とします。
  6. ans を返します。

Pythonでの実装例

以下のコードで実際の動作を確認できます。

class Solution(object):
    def longestDecomposition(self, text):
        start = 0
        end = len(text) - 1
        temp1 = ""
        temp2 = ""
        ans = 1 if len(text) & 1 else 0
        while start < end:
            temp1 += text[start]
            temp2 = text[end] + temp2
            if temp1 == temp2:
                temp1 = temp2 = ""
                ans += 2
            start += 1
            end -= 1
        if len(text) % 2 == 0 and (temp1 or temp2):
            ans += 1
        return ans

ob = Solution()
print(ob.longestDecomposition("antaprezatepzapreanta"))

入力

"antaprezatepzapreanta"

出力

11

まとめ

このアルゴリズムは、左右から1文字ずつ付け足しながら一致を判定するシンプルな貪欲法です。一致した時点でできるだけ早く分割を確定させることで、最大のチャンク数を得られます。最悪計算量は O(n²) となりますが、実装が非常に分かりやすく、LeetCode の「Longest Chunked Palindrome Decomposition」のような文字列操作系の問題にもそのまま応用できる考え方です。

  1. Pythonで文字列配列の最長共通プレフィックスを求める方法

    配列に複数の文字列が格納されている場合、それらの文字列に共通する最長共通プレフィックス(Longest Common Prefix)を見つける必要があります。ここでは、すべての文字列が小文字であると仮定します。また、共通のプレフィックスが存在しない場合は空文字列 を返すものとします。 例えば、文字列の配列が [school, schedule, scotland] のような場合、すべての文字列に共通して含まれているのは sc であるため、最長共通プレフィックスは sc となります。 解決のアプローチ この問題を解くためには、以下の手順で処理を進めます。 最初の文字列を基準(current)

  2. Pythonで整数が回文数(パリンドローム)かどうかを判定する方法

    整数が与えられたとき、それが回文数(パリンドローム)であるかどうかを判定する方法を解説します。回文数とは、前から読んでも後ろから読んでも同じ並びになる数値のことです。例えば「454」は逆順にしても「454」となるため回文数です。一方、「-565」を逆順にすると「565-」となり、マイナス記号の位置が変わるため元の数と一致せず、回文数にはなりません。解法の考え方この問題は非常にシンプルに解けます。手順は以下の通りです。1. 数値をstr()で文字列に変換する2. Pythonのスライス記法[::-1]を使って文字列を反転させる3. 元の文字列と反転した文字列を比較し、一致すればTrue、一致しな