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

Pythonで3つの文字列の最長共通部分列(LCS)の長さを求めるプログラム

問題概要

3つの文字列 s1s2s3 が与えられたとき、これらすべてに共通する最長共通部分列(LCS:Longest Common Subsequence)の長さを求めることを考えます。

たとえば、入力が以下のような場合を想定してみましょう。

  • s1 = "ababchemxde"
  • s2 = "pyakcimde"
  • s3 = "oauctime"

この場合の出力は 4 になります。これは、3つの文字列すべてに共通する最長の部分列が "acme" であり、その長さが4文字だからです。

解き方(アルゴリズム)

この問題は動的計画法(DP)を用いて効率的に解くことができます。2つの文字列に対するLCSの考え方を、3次元のDPテーブルへと拡張します。具体的な手順は以下の通りです。

  • m := s1 の長さ、n := s2 の長さ、o := s3 の長さ とします。
  • (m + 1) × (n + 1) × (o + 1) のサイズを持つ3次元行列 dp を用意し、すべて 0 で初期化します。
  • i を 1 から m まで繰り返します。
    • j を 1 から n まで繰り返します。
      • k を 1 から o まで繰り返します。
        • s1[i - 1]、s2[j - 1]、s3[k - 1] がすべて同じ文字である場合:
          dp[i][j][k] := 1 + dp[i - 1][j - 1][k - 1]
        • それ以外の場合:
          dp[i][j][k] := max(dp[i - 1][j][k], dp[i][j - 1][k], dp[i][j][k - 1])
  • 最後に dp[m][n][o] を答えとして返します。

このアルゴリズムの時間計算量・空間計算量はいずれも O(m × n × o) となります。

Pythonでの実装例

理解を深めるために、以下の実装例を見てみましょう。

class Solution:
   def solve(self, s1, s2, s3):
      m = len(s1)
      n = len(s2)
      o = len(s3)
      dp = [[[0 for i in range(o + 1)] for j in range(n + 1)] for k in range(m + 1)]
      for i in range(1, m + 1):
         for j in range(1, n + 1):
            for k in range(1, o + 1):
               if s1[i - 1] == s2[j - 1] == s3[k - 1]:
                  dp[i][j][k] = 1 + dp[i - 1][j - 1][k - 1]
               else:
                  dp[i][j][k] = max(dp[i - 1][j][k], dp[i][j - 1][k], dp[i][j][k - 1])
      return dp[m][n][o]
ob = Solution()
s1 = "ababchemxde"
s2 = "pyakcimde"
s3 = "oauctime"
print(ob.solve(s1, s2, s3))

入力

"ababchemxde", "pyakcimde", "oauctime"

出力

4
  1. Pythonで最長のバランス括弧部分列の長さを求めるプログラム

    問題概要 文字列 s が与えられます。この文字列には括弧「(」と「)」が含まれており、その中からバランスの取れた(対応関係が成立している)括弧の部分列として最も長いものを見つけ、その長さを返すことが目標です。 たとえば、入力が s = ())(()( の場合、出力は 4 になります。「(」と「)」を選び抜いて ()() というバランスの取れた部分列を作れるためです。 解法のアプローチ この問題は、文字列を後ろから走査することで線形時間で解けます。閉じ括弧を先に確保しておき、開き括弧が出てきたときに対を成立させるという発想です。手順は以下の通りです。 結果を格納する変数 res を 0 で初

  2. Pythonで文字列リストの最長共通プレフィックス(接頭辞)を求めるプログラム

    小文字で構成された文字列のリストが与えられたとき、その中に共通して含まれる最長の共通プレフィックス(接頭辞)を見つける問題を考えてみましょう。例えば、入力が [antivirus, anticlockwise, antigravity] の場合、すべての文字列に共通する先頭部分は anti なので、出力は anti となります。解決のためのアプローチこの問題は、以下の手順で解くことができます。まず、リスト words をアルファベット順にソートします。これにより、辞書順で最も近い文字列同士が隣り合うため、比較が効率的になります。共通プレフィックスを格納するための新しいリスト prefix を用