算法4.25

1、
Longest Increasing Subsequence
Given an unsorted array of integers, find the length of longest increasing subsequence.

Example:

Input: [10,9,2,5,3,7,101,18]
Output: 4
Explanation: The longest increasing subsequence is [2,3,7,101], therefore the length is 4.
Note:

There may be more than one LIS combination, it is only necessary for you to return the length.
Your algorithm should run in O(n2) complexity.

解题思路:很经典的题用来练习动态规划,首先呢我们构建一个用来保存状态的dp数组,dp[i]就相当于代表到nums[i]的最长递增子序列的长度,这个题的动归并不像斐波那契数列一样只需要保存前一个的状态就可以推导下一个的状态,而是dp[i]需要由dp[0]到dp[i-1]所有的数来共同决定dp[i]的值,那我们假设一下如果dp[i]前面的最长递增子序列已经明确,那么我们计算dp[i]时需要再次循环nums数组中i以前的数组,若找到以前的数组中找到了比nums[i]小的数,那么我们只需要找到对应的dp数组中在此处的长度加上一取最大值即可保证最后得到的dp[i]是从nums[0]到nums[i]最长的递增子序列的长度。最后输出只需要在dp数组中找出最大值直接输出即可,需要注意的是dp数组的初始化,由于到nums[i]的最长递增子序列长度就算在小,也只能是一即本身。
第二种思路:参考了一下官方题解,就是一个很简单的贪心的思路,如果我们想要得到一个最长的的上升序列,那么我们就要让这个上升子序列的坡度尽可能的小,也就是让这个上升子序列的每项之间的差值尽可能的小,这时候呢我们就必须要构建这个最长的上升序列d[],并且在我们遍历这个nums数组时更新这个最长的上升序列,我们这时候保证我们构建的这个上升序列是递增的,那么在我们遍历这个nums数组的时候,为了实现我们的贪心的思路,以curr代表当前的数,若curr大于这个上升序列的最后一项,那么直接添加curr到d[]的尾部,如果不是的话,根据我们的思路,我们要把curr插入到d[]数组中找到第一个比curr小的数并替换掉这个数前面的数。这样一直遍历到nums数组的最后,更新d[]数组最后得到的d[]数组的长度就是最长上升序列的长度,要注意的是插入curr时要在d[]数组中查找第一个比curr小的数,为了提高算法效率,使用二分查找。

Python:dp

class Solution:
    def lenthOf(self,nums):
        if len(nums)==1:
            return 1
        dp = [1]*len(nums)#这里注意要正确得初始化dp数组,明确dp数组的定义,代表着以nums[i]结尾的最长递增子序列的长度
        for i in range(len(nums)):
            for j in range(i):
                if nums[i]>nums[j]:
                    dp[i] = max(dp[i],dp[j]+1)#在所有的递增子序列中找到最大的

        res = 0
        for i in range(len(nums)):
            if dp[i]>res:
                res = dp[i]
        return res

python贪心:

class Solution:
    def lengthOfLIS(self, nums: List[int]) -> int:
        d = []#初始化最长上升子序列为空
        for n in nums:
            if not d or n > d[-1]:#延伸最长上升子序列
                d.append(n)
            else:
                l, r = 0, len(d) - 1#下面使用二分法查找元素,并插入
                loc = r#代表最后查找到的索引值
                while l <= r:
                    mid = (l + r) // 2
                    if d[mid] >= n:
                        loc = mid
                        r = mid - 1
                    else:
                        l = mid + 1
                d[loc] = n
        return len(d)

2、Edit Distance
Given two words word1 and word2, find the minimum number of operations required to convert word1 to word2.

You have the following 3 operations permitted on a word:

Insert a character
Delete a character
Replace a character
Example 1:

Input: word1 = “horse”, word2 = “ros”
Output: 3
Explanation:
horse -> rorse (replace ‘h’ with ‘r’)
rorse -> rose (remove ‘r’)
rose -> ros (remove ‘e’)
Example 2:

Input: word1 = “intention”, word2 = “execution”
Output: 5
Explanation:
intention -> inention (remove ‘t’)
inention -> enention (replace ‘i’ with ‘e’)
enention -> exention (replace ‘n’ with ‘x’)
exention -> exection (replace ‘n’ with ‘c’)
exection -> execution (insert ‘u’)

解题思路:编辑距离,要使用最少的操作数将字符串1变到字符串2,且只能使用三种操作,1、插入一个字符2、删除一个字符3、替换一个字符 这道题呢,动态规划,首先思考怎样构建这个dp数组来保存状态,使得能够从前面的状态中进行推导得出后面的状态,即状态转移方程,还有怎样定义这个dp数组。分析一下这个问题,编辑距离,如果有一个字符串a和一个字符串b,来计算他们的编辑距离,那么如果从一个空串编辑到字符串b或者一个从一个空串编辑距离到字符串a那么这个编辑距离毫无疑问就是非空串的长度,即从一空串编辑距离到一个非空串就是这个非空串的长度,那么设置一个二维数组dp数组,dp[i][j]所代表的即是从字符串a的前i位字符到字符串b的前j位字符的编辑距离,现在我们就可以从这个dp数组里面来推导编辑距离,那么初始化dp数组的dp[i][0]与dp[0][j]即为i或j,因为从一空串到一非空串的编辑距离就是非空串的长度,初始化完成后,对dp[i][j]的填充,就可以从前面的dp[i][j-1],dp[i-1][j-1],dp[i-1][j]中推导得到,那么已经完成dp数组的初始化了,那填充的工作就从dp[1][1]开始,那么从dp[i-1][j-1]开始看,那么这时候首先判断a[i]==b[j]即字符串a 的第i位与字符串b的第j位是否相等,若相等,那么根本就不需要操作,直接让dp[i][j]=dp[i-1][j-1]即可,如果不相等的话那么这时候就要从前面的三个状态dp[i][j-1],dp[i-1][j-1],dp[i-1][j]开始推导,
那么dp[i][j-1]就相当于三种基本操作中的插入操作,这时将字符串a的前i位已经转换为了b的前j-1项,那么到达dp[i][j]即是在后面插入一个字符即可,编辑距离加一,即是dp[i][j-1]+1,
Dp[i-1][j]就相当于删除操作,这时已经将a的前i-1位字符转化为b的前j位字符,那么到达dp[i][j]就将a的前i位字符切片,删去最后一位即可,这样,编辑距离加一,即是dp[i-1][j]+1
Dp[i-1][j-1]就相当于替换操作将a第i位字符替换为b的第j位字符,那么到达dp[i][j]就是编辑距离加一,dp[i-1][j-1]+1。
其实操作就是取这三者的最小值了,边界已经给出,然后一直往后面推导填充完即可,最后返回dp数组的最后一项就是结果。

class Solution:
    def minDistance(self, word1: str, word2: str) -> int:
        n1 = len(word1)
        n2 = len(word2)
        dp = [[0] * (n2 + 1) for _ in range(n1 + 1)]#初始化dp数组全部填充0
        # 第一行
        for j in range(1, n2 + 1):#初始化边界,空串转非空串
            dp[0][j] = dp[0][j-1] + 1
        # 第一列
        for i in range(1, n1 + 1):#初始化边界,非空串转空串
            dp[i][0] = dp[i-1][0] + 1
        for i in range(1, n1 + 1):
            for j in range(1, n2 + 1):
                if word1[i-1] == word2[j-1]:#如果a[i]==b[j],那么编辑距离变化为零,那肯定直接取dp[i-1][j-1]
                    dp[i][j] = dp[i-1][j-1]
                else:#取下面三者的最小值
                    dp[i][j] = min(dp[i][j-1], dp[i-1][j], dp[i-1][j-1] ) + 1
            
        return dp[-1][-1]

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值