Pythonで2つの文字列が等価かどうかを再帰的に判定する方法
問題の概要
同じ長さを持つ2つの文字列 s と t があるとします。このとき、s と t が「等価」であるかどうかを判定する必要があります。等価とみなされる条件は以下の通りです。
- 両者の文字列が完全に一致している。または、
- 文字列
sを同じサイズの2つの連続する部分文字列s1とs2に分割し、同様にtもt1とt2に分割した場合、次のいずれかが成り立てばよい:s1がt1と再帰的に等価であり、かつs2がt2と再帰的に等価であるs1がt2と再帰的に等価であり、かつs2がt1と再帰的に等価である
具体例
たとえば、入力が s = "ppqp"、t = "pqpp" の場合、出力は True になります。
s を s1 = "pp"、s2 = "qp" に分割し、t を t1 = "pq"、t2 = "pp" に分割すると、まず s1 = t2 が成り立ちます。さらに s2 と t1 をそれぞれ s21 = "q"、s22 = "p"、t11 = "p"、t12 = "q" に分割すると、s21 = t12 かつ s22 = t11 となるため、再帰的に等価であることが確認できます。
解法のアプローチ
この問題は、各文字列を「正規形」に変換してから比較することで効率的に解けます。手順は以下の通りです。
- 関数
util()を定義します。引数として文字列sを受け取ります。 sの長さが奇数の場合、それ以上分割できないためsをそのまま返します。sの左半分に対してutil()を再帰的に適用し、結果をleftとします。- 右半分についても同様に処理し、結果を
rightとします。 left + rightとright + leftのうち、辞書順で小さい方を返します。これにより、部分文字列の入れ替えによって生じうるすべてのパターンが同一の正規形にまとめられます。- メイン処理では、
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) となります。
まとめ
文字列を再帰的に半分に分割し、辞書順で最小の並びに正規化してから比較するというシンプルな発想により、複雑な等価性の判定問題をエレガントに解決できます。この手法は、分割統治法の典型的な応用例としても学習価値が高いでしょう。
-
Pythonで二分木が対称木(シンメトリックツリー)かどうかを判定するプログラム
ある二分木が与えられたとき、その木が対称木(シンメトリックツリー)であるかどうかを判定します。対称木とは、鏡像(左右反転した像)をとったときに元の木と完全に一致するような木のことです。例えば、左右の子部分木が互いに鏡写しの関係になっている木は対称木とみなされます。この判定を行うためのアプローチは以下の通りです。解法の考え方再帰的に処理を行う関数 solve(root, root) を呼び出します。同じノードを2つの引数として渡すのがポイントです。比較対象の2つのノード(node1 と node2)がどちらも空(None)の場合、True を返します。どちらか一方だけが空の場合、構造が一致してい
-
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、または