学python的第十四天---小蓝(5)

一、最长公共子序列(dp)

在这里插入图片描述
在这里插入图片描述

Maxn = 1005
dp = [[0 for _ in range(Maxn)] for _ in range(Maxn)]
if __name__ == '__main__':
    n,m=map(int,input().split())
    a= list(map(int,input().split()))
    b= list(map(int,input().split()))
    for i in range(len(a)):
        for j in range(len(b)):
            if a[i] == b[j]:
                dp[i + 1][j + 1] = dp[i][j] + 1
            else:
                dp[i + 1][j + 1] = max(dp[i + 1][j], dp[i][j + 1])
    print(dp[n][m])

二、蓝桥骑士(最长递增子序列)

在这里插入图片描述

maxn=1005
dp=[1 for _ in range(maxn)]
n=int(input())
a=list(map(int,input().split()))
ans=1
for i in range(len(a)):
    for j in range(i):
        if a[i]>a[j]:
            dp[i]=max(dp[j]+1,dp[i])
    ans=max(ans,dp[i])
print(ans)

三、蓝肽子序列(最长公共子序列)

在这里插入图片描述

def to(u):
    ans=[]
    s=""
    for i in u:
        if "A"<=i<="Z":
            ans.append(s)
            s=i
        else:
            s+=i
    ans.append(s)
    return ans[1:]#把最一开始的那个”“删除

a=input()
b=input()
a=to(a)
b=to(b)
# print(a)
# print(b)
n,m=len(a),len(b)
dp=[[0 for i in range(m+5)]for j in range(n+5)]
for i in range(len(a)):
  for j in range(len(b)):
    if a[i]==b[j]:
      dp[i+1][j+1]=dp[i][j]+1
    else:
      dp[i+1][j+1]=max(dp[i+1][j],dp[i][j+1])
print(dp[n][m])

四、合唱队形(最长递增子序列)

在这里插入图片描述

五、字符串编辑问题

在这里插入图片描述

引入一个难一点的题目:最优包含

在这里插入图片描述
这个题目是线性 DP 中比较经典的题目。

这个类型就做编辑距离,可以通过 DFS 解决,也可以通过 DP 解决。

DP 的时间复杂度低。

我们先来讲一下,编辑距离。

编辑距离为两个字符串,a 和 b 通过多少次变换,使得 a 变成 b。

我们可以做出 3 种操作。

1、删除操作,将 a[i] 从 a 中移除
2、插入操作,在 a[i] 后加上 b[j]
3、替换操作,将 a[i] 修改为 b[j]
编辑距离的状态转移类似 LCS ,但有有很大的差别。

初始状态,i=j=0,都在字符串的开头。

然后开始判断 a[i]=?b[j]

如果相同,那么就不需要修改,所以dp[i+1][j+1]=dp[i][j]

所以在a[i-1]等于b[j-1]时,dp[i][j]这个状态由dp[i-1][j-1]转移而来。

dp[i][j]=dp[i-1][j-1]

如果不同,那就需要进行三种可能的操作

1、修改操作:

a[i] 修改为 b[j], 因为编辑了一次,所以+1

dp[i+1][j+1]=dp[i][j]+1

所以在a[i-1]不等于b[j-1]时,dp[i][j]这个状态由dp[i-1][j-1]转移而来。

dp[i][j]=dp[i-1][j-1]

2、删除操作,直接把 a[i] 删除,此时转移到 dp[i][j+1] ,因为 a[i] 被删除,但是下一个字符到了 a[i] 的位置,而对应比较的位置到了b[j+1]。

所以此时状态转移到了dp[i][j+1]

dp[i][j+1]=dp[i][j]+1

因为编辑了一次,所以+1

所以在a[i-1]不等于b[j-1]时,dp[i][j]就有可能通过dp[i-1][j]转移而来。

3、插入操作,在a[i]后添加一个b[j],那么此时a[i+1]和b[j]对应,因为加了一个字符就变成了a[i+1],而且跟b[j]对应,那么下一个状态转移到了dp[i+1][j]

dp[i+1][j]=dp[i][j]+1

此时状态转移到了 dp[i+1][j]=dp[i][j]+1

因为编辑了一次,所以+1

所以在a[i-1]不等于b[j-1]时,dp[i][j]就有可能通过dp[i][j-1]转移而来。

那么不同时,我们选择他们的最小值即可。



def init(s,t):

    dp = [[0 for i in range(len(t) + 1)] for j in range(len(s) + 1)]
    for i in range(len(s) + 1):
        dp[i][0] = 0

    for j in range(1,len(t) + 1):
        dp[0][j] = 999999

    return dp

if __name__ == '__main__':
    s = list(input())
    t = list(input())

    dp=init(s,t)

    for i in range(len(s)):
        for j in range(len(t)):
            if s[i] == t[j]:
                dp[i + 1][j + 1] = dp[i][j]
            else:
                dp[i + 1][j + 1] = min(dp[i][j] + 1, dp[i][j + 1])
                dp[i + 1][j + 1] = min( dp[i + 1][j + 1] ,dp[j+1][i]+1)

    print(dp[-1][-1])

这道题目也比较简单,由于是包含关系,并不是相等关系,所以当S多余T是,不需要进行删除操作。

所以这个题目不考虑删除的那个状态转移即可。

def init(s,t):

    dp = [[0 for i in range(len(t) + 1)] for j in range(len(s) + 1)]
    for i in range(len(s) + 1):
        dp[i][0] = 0

    for j in range(1,len(t) + 1):
        dp[0][j] = 999999

    return dp

if __name__ == '__main__':
    s = list(input())
    t = list(input())

    dp=init(s,t)

    for i in range(len(s)):
        for j in range(len(t)):
            if s[i] == t[j]:
                dp[i + 1][j + 1] = dp[i][j]
            else:
                dp[i + 1][j + 1] = min(dp[i][j] + 1, dp[i][j + 1])

    print(dp[-1][-1])

猜你喜欢

转载自blog.csdn.net/qq_51408826/article/details/129958608