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

Pythonで2つの文字列が等価かどうかを再帰的に判定する方法

問題の概要

同じ長さを持つ2つの文字列 st があるとします。このとき、st が「等価」であるかどうかを判定する必要があります。等価とみなされる条件は以下の通りです。

  • 両者の文字列が完全に一致している。または、
  • 文字列 s を同じサイズの2つの連続する部分文字列 s1s2 に分割し、同様に tt1t2 に分割した場合、次のいずれかが成り立てばよい:
    • s1t1 と再帰的に等価であり、かつ s2t2 と再帰的に等価である
    • s1t2 と再帰的に等価であり、かつ s2t1 と再帰的に等価である

具体例

たとえば、入力が s = "ppqp"t = "pqpp" の場合、出力は True になります。

ss1 = "pp"s2 = "qp" に分割し、tt1 = "pq"t2 = "pp" に分割すると、まず s1 = t2 が成り立ちます。さらに s2t1 をそれぞれ s21 = "q"s22 = "p"t11 = "p"t12 = "q" に分割すると、s21 = t12 かつ s22 = t11 となるため、再帰的に等価であることが確認できます。

解法のアプローチ

この問題は、各文字列を「正規形」に変換してから比較することで効率的に解けます。手順は以下の通りです。

  1. 関数 util() を定義します。引数として文字列 s を受け取ります。
  2. s の長さが奇数の場合、それ以上分割できないため s をそのまま返します。
  3. s の左半分に対して util() を再帰的に適用し、結果を left とします。
  4. 右半分についても同様に処理し、結果を right とします。
  5. left + rightright + left のうち、辞書順で小さい方を返します。これにより、部分文字列の入れ替えによって生じうるすべてのパターンが同一の正規形にまとめられます。
  6. メイン処理では、util(s)util(t) が一致すれば True、そうでなければ False を返します。

実装コード

def util(s):
    if len(s) & 1 != 0:
        return s

    left = util(s[0:int(len(s) / 2)])
    right = util(s[int(len(s) / 2):len(s)])

    return min(left + right, right + left)

def solve(s, t):
    return util(s) == util(t)

s = "ppqp"
t = "pqpp"
print(solve(s, t))

入力

"ppqp", "pqpp"

出力

True

計算量について

このアルゴリズムの時間計算量は O(n log n) です。文字列を毎回半分に分割しながら再帰処理を行うため、再帰の深さは O(log n)、各レベルでの文字列結合と比較に O(n) かかるためです。メモリ使用量も同様に O(n log n) となります。

まとめ

文字列を再帰的に半分に分割し、辞書順で最小の並びに正規化してから比較するというシンプルな発想により、複雑な等価性の判定問題をエレガントに解決できます。この手法は、分割統治法の典型的な応用例としても学習価値が高いでしょう。

  1. Pythonで二分木が対称木(シンメトリックツリー)かどうかを判定するプログラム

    ある二分木が与えられたとき、その木が対称木(シンメトリックツリー)であるかどうかを判定します。対称木とは、鏡像(左右反転した像)をとったときに元の木と完全に一致するような木のことです。例えば、左右の子部分木が互いに鏡写しの関係になっている木は対称木とみなされます。この判定を行うためのアプローチは以下の通りです。解法の考え方再帰的に処理を行う関数 solve(root, root) を呼び出します。同じノードを2つの引数として渡すのがポイントです。比較対象の2つのノード(node1 と node2)がどちらも空(None)の場合、True を返します。どちらか一方だけが空の場合、構造が一致してい

  2. Pythonで与えられたグラフが2部グラフかどうかを判定するプログラム

    2部グラフとは無向グラフが与えられたとき、そのグラフが2部グラフ(バイパータイトグラフ)であるかどうかを判定する方法を解説します。2部グラフとは、グラフのすべての頂点を2つの集合 A と B に分割でき、グラフ内のすべての辺 {u, v} が必ず一方の端点 u が集合 A、もう一方の端点 v が集合 B に属するようなグラフのことです。つまり、同じ集合内の頂点同士を結ぶ辺(A-A や B-B)が一切存在しないグラフです。例として、次のようなグラフを考えてみましょう。この場合、頂点 [0, 4] を集合 A に、[1, 2, 3] を集合 B に分類できます。すべての辺は A から B、または