1. 程式人生 > >演算法--最長公共子序列以及子串

演算法--最長公共子序列以及子串

問題1:給定兩個字串,求其最大公共子序列

例如asbcdfg和scdfgjkl, 則返回scdfg

使用動態規劃求解,

假設s1=<x1,x2....xn>  s2=<y1,y2..ym>

令f[i][j]表示串s1以索引i結尾,串s2以索引j結尾的最長公共子序列的長度。

當i==0或者j==0時,此時s1[i]  s2[j]均為空,即有f[i][j]=0

i>=1,j>=1

當s1[i]==s2[j]時,有f[i][j] = f[i-1][j-1] + 1

當s1[i]!=s2[j]時,有f[i][j] = max(f[i-1][j], f[i][j-1])

由於字串s索引從0開始,為了便於計算,描述如下:

f[i][j]表示s1已i結尾,s2已j結尾的最大公共子序列:

i, j=0  f[i][j]=0

當s1[i]==s2[j]時,有f[i+1][j+1] = f[i][j] + 1

當s1[i]!=s2[j]時,有f[i+1][j+1] = max(f[i][j-1], f[i-1][j])

def testl():
    s1 = 'sabc'
    s2 = 'abcs'
    s = lcs(s1, s2)
    print (s)
    
def lcs(s1, s2):
    dp = [[0 for i in range(len(s1)+1)] for i in range(len(s2)+1)]
    for i in range(0, len(s1)):
        for j in range(0, len(s2)):
            if s1[i]==s2[j]:
                dp[i+1][j+1] = dp[i][j] + 1
            else:
                dp[i+1][j+1] = max(dp[i][j+1],dp[i+1][j])
    print (mat(dp))
    i = len(s1)
    j = len(s2)
    r = []
    while i>0 and j>0:
        #print (i,j,s1[i-1],s2[j-1])
        if s1[i-1]==s2[j-1]:
            r.append(s1[i-1])
            i -= 1
            j -= 1
        elif dp[i-1][j]==dp[i][j]:
            i -= 1
        else:
            j -= 1
    r.reverse()
    return r

動態規劃陣列如下所示:  表示0sabc 與0abcs的匹配關係:

現在找出最大的匹配子序列,

首先最大子序列長度值為dp[len(s1)][len(s2)],此時

若s1[i-1]==s2[j-1],即說明該字元即為公共字元,由於f[i][j] = f[i-1][j-1] + 1  可以沿對角斜向上尋找第二個字元 i--  j--

若兩者不相等,則找出dp[i][j]的來源,由於f[i+1][j+1] = max(f[i][j-1], f[i-1][j]),要麼左邊,要麼上邊,若dp[i-1][j]==dp[i][j]說明來自上面,

則i--,否則j--

最後倒序輸出序列即可。

問題2:給定兩個字串,求其最大公共子串

例如asbcdfg和scdfgjkl, 則返回cdfg  即必須是連續子串

s1=<x1,x2,,xm>

s2=<y1,y2...yn>

令f[i][j]表示s1以i結尾,s2以j結尾的最大公共子串:

i==0  j==0  f[i][j]=0

當s1[i]==s2[j]時,有f[i+1][j+1]=f[i][j]+1

當s1[i]!=s2[i]時,有f[i][j]=0

def lcs2(s1, s2):
    dp = [[0 for i in range(len(s1)+1)] for i in range(len(s2)+1)]
    row = 0
    col = 0
    max = 0
    for i in range(0, len(s1)):
        for j in range(0, len(s2)):
            if s1[i]==s2[j]:
                dp[i+1][j+1] = dp[i][j] + 1
                if(dp[i][j] + 1>max):
                    max = dp[i][j] + 1
                    row = i
                    col = j
            else:
                dp[i+1][j+1] = 0
    print (mat(dp))       
    return s1[i-max+1:i+1]

利用一個變數max來記錄最大子串長度,利用row記錄最大公共子串的最後一個字元在s1中的位置,返回最大公共子串:s1[i-max+1:i+1]