動的計画法を使用してこれを適切な方法で行うことができますが、指数時間で行う方法がわかりません。
2 つの文字列間の最大の共通サブシーケンスを探しています。注: サブシーケンスを意味し、サブストリングではなく、シーケンスを構成するシンボルが連続している必要はありません。
動的計画法を使用してこれを適切な方法で行うことができますが、指数時間で行う方法がわかりません。
2 つの文字列間の最大の共通サブシーケンスを探しています。注: サブシーケンスを意味し、サブストリングではなく、シーケンスを構成するシンボルが連続している必要はありません。
動的プログラミングコードのテーブル内のルックアップを再帰呼び出しに置き換えるだけです。言い換えると、LCS問題の再帰的定式化を実装するだけです。
編集
擬似コード(実際にはほとんどPython):
def lcs(s1, s2):
if len(s1)==0 or len(s2)==0: return 0
if s1[0] == s2[0]: return 1 + lcs(s1[1:], s2[1:])
return max(lcs(s1, s2[1:]), lcs(s1[1:], s2))
文字列 A と文字列 B。再帰アルゴリズムです。単純かもしれませんが、単純です。
A の最初の文字を見てください。これは共通のシーケンスであるかどうかのどちらかです。「not」オプションを検討するときは、最初の文字を削除して再帰的に呼び出します。'is in a common sequence' オプションを検討するときは、それを削除し、B の先頭から B の同じ文字まで (同じ文字を含む) も削除します。いくつかの擬似コード:
def common_subsequences(A,B, len_subsequence_so_far = 0):
if len(A) == 0 or len(B) == 0:
return
first_of_A = A[0] // the first letter in A.
A1 = A[1:] // A, but with the first letter removed
common_subsequences(A1,B,len_subsequence_so_far) // the first recursive call
if(the_first_letter_of_A_is_also_in_B):
Bn = ... delete from the start of B up to, and including,
... the first letter which equals first_of_A
common_subsequences(A1,Bn, 1+len_subsequence_so_far )
それから始めて、これまでに見つかった最長のサブシーケンスを記憶することで最適化し、現在の関数がそれを超えることができない場合 (つまり、min(len(A), len(B))+len_subsequence_so_far
これまでに見つかった最長の長さよりも小さい場合) に戻ります。
2 つの文字列a
とb
lengthがあるとしn
ます。最長の共通サブシーケンスは、 stringa
にも存在するstring の最長のサブシーケンスになりますb
。
したがって、可能なすべてのサブシーケンスを反復処理して、a
それが にあることを確認できb
ます。
このための高レベルの擬似コードは次のようになります。
for i=n to 0
for all length i subsequences s of a
if s is a subsequence of b
return s
指数時間を達成するには、両方の配列のすべてのサブシーケンスを生成し、それぞれを比較するだけで十分です。同一の 2 つを照合する場合は、それらの長さが現在の最大値よりも大きいかどうかを確認します。擬似コードは次のようになります。
Generate all subsequences of `array1` and `array2`.
for each subsequence of `array1` as s1
for each subsequece of `array2` as s2
if s1 == s2 //check char by char
if len(s1) > currentMax
currentMax = len(s1)
for i = 0; i < 2^2; i++;
それは絶対に最適ではありません。しようともしない。ただし、質問は非常に非効率的なアルゴリズムに関するものであるため、提供しました。
基本的に、動的プログラミング パラダイムを使用しないと、指数関数的な時間に到達します。これは、部分的な値を保存しないことにより、部分的な値を複数回再計算しているためです。
int lcs(char[] x, int i, char[] y, int j) {
if (i == 0 || j == 0) return 0;
if (x[i - 1] == y[j - 1]) return lcs(x, i - 1, y, j - 1) + 1;
return Math.max(lcs(x, i, y, j - 1), lcs(x, i - 1, y, j));
}
print(lcs(x, x.length, y, y.length);
以下は部分再帰ツリーです。
lcs("ABCD", "AFDX")
/ \
lcs("ABC", "AFDX") lcs("ABCD", "AFD")
/ \ / \
lcs("AB", "AFDX") lcs("AXY", "AFD") lcs("ABC", "AFD") lcs("ABCD", "AF")
最悪のケースは、LCS の長さが 0 の場合です。これは、共通のサブシーケンスがないことを意味します。その場合、すべての可能なサブシーケンスが調べられ、サブシーケンスがありますO(2^n)
。