蓝桥杯 最优包含 编辑距离【第十届】【决赛】【B组】 DP python

Hello

大家好,我是 @愿此后再无WA,可以叫我小A,也可以叫我愿愿,一位阳光帅小伙,对算法领域比较感兴趣。如果我的文章对您有用,欢迎持续关注,我们一起进步!

最优包含

蓝桥杯 最优包含 编辑距离【第十届】【决赛】【B组】 DP python_第1张图片

分析

这道题与经典面试题“编辑距离”非常相似,可以说是它的迷你版,我们先看一下那道题。
蓝桥杯 最优包含 编辑距离【第十届】【决赛】【B组】 DP python_第2张图片
蓝桥杯 最优包含 编辑距离【第十届】【决赛】【B组】 DP python_第3张图片

编辑距离算法被数据科学家广泛应用,是用作机器翻译和语音识别评价标准的基本算法。

最直观的方法是暴力检查所有可能的编辑方法,取最短的一个。所有可能的编辑方法达到指数级,但我们不需要进行这么多计算,因为我们只需要找到距离最短的序列而不是所有可能的序列。

在此我还是建议大家先学会这道经典例题再来做之后的决赛题,如果不明白上面问题的同学可以点击这里查看leetcode 72.编辑距离 题解。

在编辑距离中要将一个字符串变成另外一个字符串,那么转变的过程就有四种状态:删除,增加,修改,不变。而不变操作的前提条件是字符串1的第i个元素与字符串2的第j个元素相等。而另外三种情况的前提条件就是他两不等。以下图为例,
蓝桥杯 最优包含 编辑距离【第十届】【决赛】【B组】 DP python_第4张图片
既然他们不等,然后又要让他们相等的话可以进行删除,修改,插入操作,如果删除操作的话
蓝桥杯 最优包含 编辑距离【第十届】【决赛】【B组】 DP python_第5张图片
那么就是看Str1[0] ~ Str1[i-1] 与 Str2[0] ~ Str2[j] 的修改次数加上1(本次修改的次数)

也能进行修改操作
蓝桥杯 最优包含 编辑距离【第十届】【决赛】【B组】 DP python_第6张图片
经过修改后Str1[i] 的元素与Str2[j]元素相等了,那么就看Str1[0] ~ Str1[i-1] 与 Str2[0] ~ Str2[j-1] 的修改次数然后加上1。

还能进行插入操作,
蓝桥杯 最优包含 编辑距离【第十届】【决赛】【B组】 DP python_第7张图片
执行插入操作后相当于新插入的元素与Str2[j]给抵消掉了,所以这里就是看Str1[0] ~ Str1[i] 与 Str2[0] ~ Str2[j-1] 的修改次数然后加上1。

经过如上分析,如果Str1[i] 与 Str2[j] 不相等的话就要比较三次操作,看哪个操作次数较少就选哪个操作,最后加上1即可。

除此之外还需考虑特殊情况,如目标串长度为0时,我们就要对初始串进行若干次删除操作,删除的次数就是初始串第i个位置往前的长度;如果初始串长度是0,那么就要对初始串进行若干次插入操作,插入的次数就是目标串第j位置往前的长度。

思路

ok,了解了编辑距离之后,最优包含这道题就显得稍微简单些,毕竟跟前面挺像的而且只涉及修改操作,尽管如此我们还是不能够掉以轻心。与前面不同的是,这里是初始串是目标串的子序列,而不是要变成目标串,那么这样的话初始串里面的元素就有两种操作修改或者不修改(如果Str1[i] 与 Str2[j] 相等的话那当然就不用修改这位置,那修改次数就是看Str1[0] ~ Str1[i-1] 与 Str2[0] ~ Str2[j-1] 的修改次数了。),涉及到修改或不修改,那么这里就很有讲究了,就是初始化问题,与前面不同,因为前面要进行二次初始化,即创建N x M dp列表的时候初始化一次,考虑特殊情况的时候初始化一次。

第一次初始化是这样的,XX表示初始化成啥都行
蓝桥杯 最优包含 编辑距离【第十届】【决赛】【B组】 DP python_第8张图片

第二次初始化是这样的
蓝桥杯 最优包含 编辑距离【第十届】【决赛】【B组】 DP python_第9张图片
然后 从一行一列开始选取它左边 / 上边 / 左上边元素。

而在最优包含中,它只能够选择左边(不变)和左上边(修改,如果要修改的话次数要加1,)的元素,取最小值。(我定义的位置长串S是在横行,短串T在竖行,遍历顺序是一行行遍历,如上图,如果长短串位置反过来或者遍历顺序是一列列遍历的话就要选择上边和左上边的元素了。)如果初始化的时候全初始化成0的话,那么以刚才的逻辑,就会取最小值0全部都不修改,然后结果就是0了,显然与结果不符,所以我们就要限制它,不能每次都不选,因此初始化的时候要对每个方格初始化成最大值inf,但如果要修改值的话就要取左上边的元素,全是inf输出的结果当然也是inf,所以开始的时候要让左上角的元素都初始化成0才能符合我们判断。那么该初始化竖行好呢还是初始化横行好?两个都可以的,取决于个人习惯,如果你是一行行遍历就要初始化第0行的所有元素成0了,如果是一列列遍历的话就要初始化第0列的所有元素成0了。

讲完上面的逻辑,现在我们看一下代码实现。

代码

S = input()
T = input()

M, N = len(T), len(S)
dp = [[float("inf") for i in range(N+1)] for j in range(M+1)]

for i in range(N+1):
    dp[0][i] = 0

for i in range(1,M+1):
    for j in range(1,N+1):
        if S[j-1] == T[i-1]:
            dp[i][j] = dp[i-1][j-1]
        else:
            dp[i][j] = min(dp[i-1][j-1]+1,dp[i][j-1])

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

你可能感兴趣的:(刷题,蓝桥杯,算法)