最长公共子序列

题目:最长公共子序列(子串) ---- LCS
解法:动态规划
思路:设dp[i][j]为以第一个字符串的前i个,第二个字符串的前j个字符构成的最长公共子串,设这两个字符串分别为AB,则当A[i]==B[j]时,dp[i][j]=dp[i-1][j-1]+1;若A[i]!=B[j],则dp[i][j]=max(dp[i-1][j],dp[i][j-1])
此处解释一下当 A[i]!=B[j]时,dp[i][j]= max(dp[i-1][j],dp[i][j-1])的意义:
即当 A[i]!=B[j]时,取A[0:i-1]与B[0:j],A[0:i]与B[0:j-1]这两种情况下公共子串更长的那一种。

代码
def LCS(A,B):
    length1 = len(A)
    length2 = len(B)
    if length1 == 0 or length2 == 0:
        return 0
    dp = [[0 for j in range(length2+1)] for i in range(length1+1)]
    for i in range(1,length1+1):
    	for j in range(1,length2+1):
    		if A[i-1] == B[j-1]:
    			dp[i][j] = dp[i-1][j-1] + 1
    		else:
    			dp[i][j] = max(dp[i-1][j],dp[i][j-1])
    return dp[-1][-1]

变形一 要求记录最长的那个序列

为记录路径(即获得那个子序列),用一个2维矩阵path,首先path全初始化为0,然后在更新dp的同时按以下规则更新path

  1. path[i][j]=1 ifA[i]==B[j]
  2. path[i][j]=2 if A[i]!=B[j] and dp[i-1][j]>=dp[i][j-1]
  3. path[i][j]=3 if A[i]!=B[j] and dp[i-1][j]<dp[i][j-1]

回溯:
path的最后一个元素开始,判断path[i][j]的值,
若为1,则往[i-1][j-1]继续回溯,回溯完成后记录A[i-1]的值或B[j-1]的值;(在回溯完成后记录保证了得到的子序列和原来的顺序相同,而不是倒序的)
若为2,则往[i-1][j]继续回溯;
若为3,则往[i][j-1]继续回溯;
代码如下:

def LCS(A,B):
    length1 = len(A)
    length2 = len(B)
    if length1 == 0 or length2 == 0:
        return 0,[]
    dp = [[0 for j in range(length2+1)] for i in range(length1+1)]
    path = [[0 for j in range(length2+1)] for i in range(length1+1)]
    for i in range(1,length1+1):
        for j in range(1,length2+1):
            if A[i-1] == B[j-1]:
                dp[i][j] = dp[i-1][j-1] + 1
                path[i][j] = 1
            else:
                if dp[i-1][j]>=dp[i][j-1]:
                    dp[i][j] = dp[i-1][j]
                    path[i][j] = 2
                else:
                    dp[i][j] = dp[i][j-1]
                    path[i][j] = 3
                    
    #common_substr = ''
    # 用一个列表,同时适配序列和字符串两种情况
    res = []
    def back_track(row,col):
        if row == 0 or col == 0:
            return 
        if path[row][col] == 1:
            # res.append(A[row-1])   # 放在这里的话,得到的序列是倒序的
            back_track(row-1,col-1)
            res.append(A[row-1])
        elif path[row][col] == 2:
            back_track(row-1,col)
        else:
            back_track(row,col-1)
    back_track(length1,length2)
    return dp[-1][-1],res

变形二 要求连续,即最长公共连续子序列

状态转移:如果A[i]==B[j]dp[i][j]=dp[i-1][j-1]+1;否则,dp[i][j]=0,然后记录dp[i][j]的最大值即可。

代码
def LCCS(A,B):
    length1 = len(A)
    length2 = len(B)
    if length1 == 0 or length2 == 0:
        return 0
    dp = [[0 for j in range(length2+1)] for i in range(length1+1)]
    max_val = 0
    for i in range(1,length1+1):
    	for j in range(1,length2+1):
    		if A[i-1] == B[j-1]:
    			dp[i][j] = dp[i-1][j-1] + 1
    			max_val = max(max_val,dp[i][j])
    return max_val

变形二之变形,要求记录最长的连续子串。

思路:可以在更新max_val的时候同时记录A的索引或B的索引,此处以记录A的索引为例,假设最后一次更新max_val时的索引max_index=i,则A[max_index-max_val:max_index]即为公共子串。代码如下:

def LCCS(A,B):
    length1 = len(A)
    length2 = len(B)
    if length1 == 0 or length2 == 0:
        return 0
    dp = [[0 for j in range(length2+1)] for i in range(length1+1)]
    max_val = 0
    max_index = 0
    for i in range(1,length1+1):
    	for j in range(1,length2+1):
    		if A[i-1] == B[j-1]:
    			dp[i][j] = dp[i-1][j-1] + 1
    			if dp[i][j]>max_val:
    				max_val = dp[i][j]
    				max_index = i
    return max_val,A[max_index-max_val:max_index]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值