web-dev-qa-db-ja.com

Pythonの最長共通部分列

2つの文字列間の最長共通部分列を見つけようとしています。

私はこのチュートリアルを見ました https://www.youtube.com/watch?v=NnD96abizww

そして書いた:

# Longest Common Subsequence

def lcs(s1, s2):
    matrix = [ [0 for x in range(len(s2))] for x in range(len(s1)) ]
    cs = ""
    for i in range(len(s1)):
        for j in range(len(s2)):
            if s1[i]==s2[j]:
                if i==0 or j==0:
                    matrix[i][j] = 1
                    cs += s1[i]
                else:
                    matrix[i][j] = matrix[i-1][j-1] + 1
                    cs += s1[i]
            else:
                if i==0 or j==0:
                    matrix[i][j] = 0
                else:
                    matrix[i][j] = max(matrix[i-1][j], matrix[i][j-1])

    return matrix[len(s1)-1][len(s2)-1], cs


print(lcs("abcdaf", "acbcf"))  



I get (3, 'abccaf')

これは明らかに間違っており、4abcfである必要があります。

どのステップが間違っているのかわからない。一般的な質問の1つは、プログラマーがこの種の質問を「取得」するのに通常どのくらいの時間がかかるかということです。

4
mourinho

コードには、アルゴリズムが間違った答えを出力する原因となる2つの主な問題があります。

16行目の_if i == 0 or j == 0_

この例ではアルゴリズムが_s1[1] != s2[j]_を設定していますが、「ab」と「a」の最長共通部分列の長さが1であるため、ビデオの直後にこの行が_matrix[0][1] = 0_の場合は意味がないことがわかります。したがって、このifステートメントを削除する必要があります。その間、max(matrix[i-1][j], matrix[i][j-1])が_i == 0_または_j == 0_に対して何をするかを考慮する必要があります。現在、2つの異なるアプローチがあります。

  1. 明示的なもの:

    _max(matrix[i-1][j] if i != 0 else 0, 
        matrix[i][j-1] if j != 0 else 0)
    _
  2. 暗黙的なもの:

    _max(matrix[i-1][j], matrix[i][j-1])
    _

    Pythonでは、リストの最後の項目を取得するために負のインデックスが使用され、この場合、これらの項目は0であるため、これは機能します。

11/14行の_cs += s1[i]_

たとえば、「a」と「abcd」の最長共通部分列が「a」であることがわかった場合、アルゴリズムは「a」と「abcda」の最長共通部分列を「aa」として設定しますが、これは意味がありません。私はなぜそれがそのように機能しないのかを説明するのに苦労しているので、おそらく http:/ /pythontutor.com/visualize.html

解決

両方の問題にアプローチするには、マトリックスを使用して、小さな問題で見つかった最長共通部分列を格納できます。あなたはこれで終わります:

_def lcs(s1, s2):
    matrix = [["" for x in range(len(s2))] for x in range(len(s1))]
    for i in range(len(s1)):
        for j in range(len(s2)):
            if s1[i] == s2[j]:
                if i == 0 or j == 0:
                    matrix[i][j] = s1[i]
                else:
                    matrix[i][j] = matrix[i-1][j-1] + s1[i]
            else:
                matrix[i][j] = max(matrix[i-1][j], matrix[i][j-1], key=len)

    cs = matrix[-1][-1]

    return len(cs), cs

print(lcs("abcdaf", "acbcf"))  
_

この特定の実装は、1つの可能な結果のみを返します。演習として、最も長い一般的なシーケンスをすべて提供するアルゴリズムの実装を試みることができます。たぶん ウィキペディアのページ גלעדברקןによって提案されたように見てください

コードが機能しない理由を「取得」するのにどのくらい時間がかかりますか?

明らかに明確な答えはありません。例について考えることは常に役立ちます。アルゴリズムの場合、ウィキペディアには多くの場合、実装の基礎となる優れた擬似コードがあります。アルゴリズムに関連する概念とデータ構造に精通している場合は、1日以内に実装できるはずです(ただし、私は間違いなく専門家ではありません)。一般に、コード内の論理的なバグの検索は、コードのサイズによっては数日かかる場合があります。この種の構造化されたアルゴリズム的および数学的思考を実践するには、 projecteuler.net を強くお勧めします。

5
BurningKarl

組み込みのソリューションをお探しの方へ:

from difflib import SequenceMatcher

str_a = "xBCDxFGxxxKLMx"
str_b = "aBCDeFGhijKLMn"
s = SequenceMatcher(None, str_a, str_b)

lcs = ''.join([str_a[block.a:(block.a + block.size)] for block in s.get_matching_blocks()])
# lcs = 'BCDFGKLM'
2
lmcarreiro

次のコードセグメントを使用してLCSの長さを取得するだけで、最大長が14であることがわかります。したがって、BurningKarlのソリューションが機能します。

def longestCommonSequence(s1, s2, s1Index, s2Index, arr):
    if s1Index ==-1 or s2Index == -1:
        return 0
    if(arr[s1Index][s2Index] != None):
        return arr[s1Index][s2Index]

    if s1[s1Index] == s2 [s2Index]:
        result = 1+ longestCommonSequence(s1, s2, s1Index -1, s2Index -1, arr)
    else:
        result= max(longestCommonSequence(s1, s2, s1Index -1, s2Index, arr), longestCommonSequence(s1, s2, s1Index, s2Index -1, arr))
    arr[s1Index][s2Index] = result
    return result   

s1="AAACCGTGAGTTATTCGTTCTAGAA" 
s2="CACCCCTAAGGTACCTTTGGTTC" 

a = [[None for i in range(len(s2))] for j in range(len(s1))]
print(longestCommonSequence(s1, s2, len(s1)-1, len(s2)-1, a))

0
Xi Yan

前の答えは正しい解決策ではありません(ほとんどの場合に機能しますが)、正しい解決策はマトリックスを作成してからマトリックスをバックトラックすることですここで前の答えが失敗した場合「AAACCGTGAGTTATTCGTTCTAGAA」「CACCCCTAAGGTACCTTTGGTTC」最長共通部分列は「ACCTAGTACTTTG」ですが、前のコードは正しい result を返しません。

(以下のコードはpython 3)を使用します

enter image description here

def my_lcs(x_string, y_string):
    matrix = [[0 for each_x in range(0,len(y_string)+1)] for each_y in range(0,len(x_string)+1)]
    for each_y in range(len(y_string)):
        for each_x in range(len(x_string)):
            prev_x =each_x-1
            prev_y =each_y-1
            if(x_string[prev_x]== y_string[prev_y]):
                matrix[each_x][each_y] = matrix[prev_x][prev_y] + 1
            else:
                matrix[each_x][each_y] = max(matrix[prev_x][each_y] , matrix[each_x][prev_y])
    return matrix



#print /backtrack 
def print_lcs( mtrx, x_string, y_string ):
    result = []
    x, y = len( x_string ), len(y_string)
    while x> 0 and y > 0:
      if x_string[x- 1] == y_string[y - 1]:
          result.append( x_string[x- 1] )
          x-= 1
          y -= 1
      Elif mtrx[x][y - 1] >= mtrx[x- 1][y]:
           y -= 1
      else:
          x-= 1
    print(result[::-1] )

inputa, inputb="1ab", "2ab" #ab
#inputa, inputb="AAACCGTGAGTTATTCGTTCTAGAA", "CACCCCTAAGGTACCTTTGGTTC" #ACCTAGTACTTTG 
#inputa, inputb="houseboat", "computer"#oue
#inputa, inputb="2193588", "21943588" #2193588
#inputa, inputb="ABCBDAB", "BDCABA" #BDAB
result= my_lcs(inputa, inputb)   
print_lcs(result,inputa, inputb)  

enter image description here

これは https://www.cs.fsu.edu/~burmeste/slideshow/chapter16/16-3.html および http://www.columbia.edu/ 〜cs2035/courses/csor4231.F11/lcs.pdf

0
grepit