最長公共子序列你學會了嗎


最長公共子序列

問題描述

給定兩個字符串str1和str2,輸出兩個字符串的最長公共子序列。如果最長公共子序列為空,則返回"-1"。目前給出的數據,僅僅會存在一個最長的公共子序列。

示例:

輸入:"abcde","ace"

輸出:"ace"

分析問題

首先,我們先來看一下什么是子序列。一個字符串的子序列是指這樣一個新的字符串:它是由原字符串在不改變字符的相對順序的情況下刪除某些字符(也可以不刪除任何字符)后組成的新字符串。例如,“ace”是“abcde”的子序列,但是“aec”不是“abcde”的子序列。這個問題和求最長公共子串類似,我們也可以采用動態規划的方法來求解。

我們假設str1[0,i-1]str2[0,j-1]的最長公共子序列為dp[i-1] [j-1],那么str1[0,i]str2[0,j]的最長公共子序列等於多少呢?這主要取決於字符str1[i]str2[j]是否相等,如果相等,則最長公共子序列會加1,即dp[i] [j] = dp[i-1] [j-1] + 1。如果不相等,則dp[i] [j] = max(dp[i-1] [j],dp[i] [j-1])。有了動態轉移方程,我們只需要遍歷字符串,逐步填寫狀態轉移矩陣就好了。

下面我們來看一下代碼如何實現。

def longestCommonSubsequence(str1,str2):
    n=len(str1)
    m=len(str2)
    dp=[[0 for _ in range(n)] for _ in range(m)]

    #處理邊界條件
    if str1[0]==str2[0]:
        dp[0][0] = 1

    for i in range(n):
        if(str2[0]==str1[i]):
            dp[0][i]=1
        else:
            dp[0][i]=dp[0][i-1]

    for j in range(m):
        if(str1[0]==str2[j]):
            dp[j][0] = 1
        else:
            dp[j][0] = dp[j-1][0]


    for i in range(1, m):
        for j in range(1, n):
            if str2[i]==str1[j]:
                dp[i][j] = dp[i - 1][j - 1] + 1
            else:
                dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])

    return dp[m-1][n-1]

由於題目要求是輸出具體的子序列,所以我們還需要根據狀態轉移矩陣逆推回去。

通過狀態轉移方程,我們可以知道dp[i] [j]要么從dp[i-1] [j-1] +1 求得,要么通過dp[i] [j-1] 和 dp[i-1] [j]的最大值得到,所以我們可以從dp[m-1] [n-1] 倒推出str1和str2的最大公共子序列。如下圖所示。

我們來看一下完整的代碼實現。

def LCS(self , str1 , str2 ):
        n=len(str1)
        m=len(str2)
        if n==0 or m==0:
           return -1
        dp=[[0 for _ in range(n)] for _ in range(m)]

        #處理邊界條件
        if str1[0]==str2[0]:
            dp[0][0] = 1

        for i in range(n):
            if(str2[0]==str1[i]):
                dp[0][i]=1
            else:
                dp[0][i]=dp[0][i-1]

        for j in range(m):
            if(str1[0]==str2[j]):
                dp[j][0] = 1
            else:
                dp[j][0] = dp[j-1][0]


        for i in range(1, m):
            for j in range(1, n):
                if str2[i]==str1[j]:
                    dp[i][j] = dp[i - 1][j - 1] + 1
                else:
                    dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])


        #通過狀態轉移矩陣倒推出子序列
        tmp=[]
        s1_index=n-1
        s2_index=m-1
        while s1_index !=0 and s2_index !=0:

            if str1[s1_index]==str2[s2_index]:
                tmp.append(str1[s1_index])
                s1_index=s1_index-1
                s2_index=s2_index-1
            else:
                if dp[s2_index-1][s1_index]>dp[s2_index][s1_index-1]:
                    s2_index = s2_index - 1
                else:
                    s1_index = s1_index - 1

        #單獨處理第0行0列
        if s1_index==0:
            while s2_index>=0:
                if str1[0]==str2[s2_index]:
                    tmp.append(str1[0])
                    break
                s2_index=s2_index-1

        else:
            while s1_index>=0:
                if str1[s1_index]==str2[0]:
                    tmp.append(str1[s1_index])
                    break
                s1_index=s1_index-1
        if len(tmp)==0:
            return -1
        else:
            return "".join(tmp[::-1])

該算法的時間復雜度和空間復雜度都是O(n^2)。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM