首页 > 其他分享 >代码随想录|动态规划

代码随想录|动态规划

时间:2023-07-04 21:14:17浏览次数:40  
标签:代码 随想录 len range word1 word2 字符串 动态 dp

 392.判断子序列 

115.不同的子序列  

 583. 两个字符串的删除操作 

 72. 编辑距离 

编辑距离总结篇


392.判断子序列

和昨天的最长重复子串一样,只要计算两者的重复长度是不是和s一样就行了。但是还是不如双指针的时间复杂度

O(nm)

O(nm)

class Solution:
    def isSubsequence(self, s: str, t: str) -> bool:
        # n = len(s)
        # m = len(t)
        # i = 0
        # j = 0
        # for i in range(n):
        #     while(j<m and t[j]!=s[i]):
        #         j += 1
        #     if j >= m:
        #         return False
        #     j += 1
        # return True
        
        n = len(s)
        m = len(t)

        dp = [[0 for _ in range(m+1)] for _ in range(n+1)]
        for i in range(1, n+1):
            for j in range(1, m+1):
                if s[i-1] == t[j-1]:
                    dp[i][j] = dp[i-1][j-1] + 1
                else:
                    dp[i][j] = max(dp[i-1][j], dp[i][j-1])
        if dp[n][m] == n:
            return True
        return False

 

 


115.不同的子序列

很值得再做一遍的题目, 二维dp。在思考好dp的定义以后, 对于dp方程的推导和初始化的定义都是需要考虑的对象,深刻理解dp方程的定义是关键

class Solution:
    def numDistinct(self, s: str, t: str) -> int:
        n = len(s)
        m = len(t)
        dp = [[0 for _ in range(m+1)] for _ in range(n+1)]
        for i in range(n+1):
            dp[i][0] = 1

        for j in range(1, m+1):
            for i in range(1, n+1):
                if s[i-1] == t[j-1]:
                    dp[i][j] = dp[i-1][j-1] + dp[i-1][j]
                else:
                    dp[i][j] = dp[i-1][j]
        return dp[n][m]

 


583. 两个字符串的删除操作

和昨天的最长重复子串一样,只要计算两者的重复长度是不是和s一样就行了,剩下的直接删掉。

class Solution:
    def minDistance(self, word1: str, word2: str) -> int:
        n = len(word1)
        m = len(word2)
        dp = [[0 for _ in range(m+1)] for _ in range(n+1)]
        for i in range(1, n+1):
            for j in range(1, m+1):
                if word1[i-1] == word2[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 n+m-2*dp[n][m]

 


72. 编辑距离

感觉还挺难的 最主要的部分依旧是推导dp的公式以及初始化的部分

将Given two strings word1 and word2, return the minimum number of operations required to convert word1 to word2.

思考如何变化以及dp的单一还是很重要的

class Solution:
    def minDistance(self, word1: str, word2: str) -> int:
        m = len(word1)
        n = len(word2)
        dp = [[0 for _ in range(n+1)] for _ in range(m+1)]
        for i in range(1, m+1):
            dp[i][0] = i
        for j in range(1, n+1):
            dp[0][j] = j

        for i in range(1, m+1):
            for j in range(1, n+1):
                if word1[i-1] == word2[j-1]:
                    dp[i][j] = dp[i-1][j-1]
                else:
                    dp[i][j] = min(
                        dp[i-1][j]+1,
                        dp[i][j-1]+1,
                        dp[i-1][j-1]+1
                    )
        return dp[m][n]

动态规划之编辑距离总结篇

判断子序列

动态规划:392.判断子序列 (opens new window)给定字符串 s 和 t ,判断 s 是否为 t 的子序列。

这道题目 其实是可以用双指针或者贪心的的,但是我在开篇的时候就说了这是编辑距离的入门题目,因为从题意中我们也可以发现,只需要计算删除的情况,不用考虑增加和替换的情况。

  • if (s[i - 1] == t[j - 1])
    • t中找到了一个字符在s中也出现了
  • if (s[i - 1] != t[j - 1])
    • 相当于t要删除元素,继续匹配

状态转移方程:

if (s[i - 1] == t[j - 1]) dp[i][j] = dp[i - 1][j - 1] + 1;
else dp[i][j] = dp[i][j - 1];






不同的子序列

动态规划:115.不同的子序列 (opens new window)给定一个字符串 s 和一个字符串 t ,计算在 s 的子序列中 t 出现的个数。

本题虽然也只有删除操作,不用考虑替换增加之类的,但相对于动态规划:392.判断子序列 (opens new window)就有难度了,这道题目双指针法可就做不了。

当s[i - 1] 与 t[j - 1]相等时,dp[i][j]可以有两部分组成。

一部分是用s[i - 1]来匹配,那么个数为dp[i - 1][j - 1]。

一部分是不用s[i - 1]来匹配,个数为dp[i - 1][j]。

这里可能有同学不明白了,为什么还要考虑 不用s[i - 1]来匹配,都相同了指定要匹配啊。

例如: s:bagg 和 t:bag ,s[3] 和 t[2]是相同的,但是字符串s也可以不用s[3]来匹配,即用s[0]s[1]s[2]组成的bag。

当然也可以用s[3]来匹配,即:s[0]s[1]s[3]组成的bag。

所以当s[i - 1] 与 t[j - 1]相等时,dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j];

当s[i - 1] 与 t[j - 1]不相等时,dp[i][j]只有一部分组成,不用s[i - 1]来匹配,即:dp[i - 1][j]

所以递推公式为:dp[i][j] = dp[i - 1][j];

状态转移方程:

if (s[i - 1] == t[j - 1]) {
    dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j];
} else {
    dp[i][j] = dp[i - 1][j];
}






两个字符串的删除操作

动态规划:583.两个字符串的删除操作 (opens new window)给定两个单词 word1 和 word2,找到使得 word1 和 word2 相同所需的最小步数,每步可以删除任意一个字符串中的一个字符。

本题和动态规划:115.不同的子序列 (opens new window)相比,其实就是两个字符串可以都可以删除了,情况虽说复杂一些,但整体思路是不变的。

  • 当word1[i - 1] 与 word2[j - 1]相同的时候
  • 当word1[i - 1] 与 word2[j - 1]不相同的时候

当word1[i - 1] 与 word2[j - 1]相同的时候,dp[i][j] = dp[i - 1][j - 1];

当word1[i - 1] 与 word2[j - 1]不相同的时候,有三种情况:

情况一:删word1[i - 1],最少操作次数为dp[i - 1][j] + 1

情况二:删word2[j - 1],最少操作次数为dp[i][j - 1] + 1

情况三:同时删word1[i - 1]和word2[j - 1],操作的最少次数为dp[i - 1][j - 1] + 2

那最后当然是取最小值,所以当word1[i - 1] 与 word2[j - 1]不相同的时候,递推公式:dp[i][j] = min({dp[i - 1][j - 1] + 2, dp[i - 1][j] + 1, dp[i][j - 1] + 1});

状态转移方程:

if (word1[i - 1] == word2[j - 1]) {
    dp[i][j] = dp[i - 1][j - 1];
} else {
    dp[i][j] = min({dp[i - 1][j - 1] + 2, dp[i - 1][j] + 1, dp[i][j - 1] + 1});
}






编辑距离

动态规划:72.编辑距离 (opens new window)给你两个单词 word1 和 word2,请你计算出将 word1 转换成 word2 所使用的最少操作数 。

编辑距离终于来了,有了前面三道题目的铺垫,应该有思路了,本题是两个字符串可以增删改,比 动态规划:判断子序列 (opens new window)动态规划:不同的子序列 (opens new window)动态规划:两个字符串的删除操作 (opens new window)都要复杂的多。

在确定递推公式的时候,首先要考虑清楚编辑的几种操作,整理如下:

  • if (word1[i - 1] == word2[j - 1])
    • 不操作
  • if (word1[i - 1] != word2[j - 1])

也就是如上四种情况。

if (word1[i - 1] == word2[j - 1]) 那么说明不用任何编辑,dp[i][j] 就应该是 dp[i - 1][j - 1],即dp[i][j] = dp[i - 1][j - 1];

此时可能有同学有点不明白,为啥要即dp[i][j] = dp[i - 1][j - 1]呢?

那么就在回顾上面讲过的dp[i][j]的定义,word1[i - 1] 与 word2[j - 1]相等了,那么就不用编辑了,以下标i-2为结尾的字符串word1和以下标j-2为结尾的字符串word2的最近编辑距离dp[i - 1][j - 1] 就是 dp[i][j]了。

在下面的讲解中,如果哪里看不懂,就回想一下dp[i][j]的定义,就明白了。

在整个动规的过程中,最为关键就是正确理解dp[i][j]的定义!

if (word1[i - 1] != word2[j - 1]),此时就需要编辑了,如何编辑呢?

操作一:word1增加一个元素,使其word1[i - 1]与word2[j - 1]相同,那么就是以下标i-2为结尾的word1 与 i-1为结尾的word2的最近编辑距离 加上一个增加元素的操作。

即 dp[i][j] = dp[i - 1][j] + 1;

操作二:word2添加一个元素,使其word1[i - 1]与word2[j - 1]相同,那么就是以下标i-1为结尾的word1 与 j-2为结尾的word2的最近编辑距离 加上一个增加元素的操作。

即 dp[i][j] = dp[i][j - 1] + 1;

这里有同学发现了,怎么都是添加元素,删除元素去哪了。

word2添加一个元素,相当于word1删除一个元素,例如 word1 = "ad" ,word2 = "a",word2添加一个元素d,也就是相当于word1删除一个元素d,操作数是一样!

操作三:替换元素,word1替换word1[i - 1],使其与word2[j - 1]相同,此时不用增加元素,那么以下标i-2为结尾的word1 与 j-2为结尾的word2的最近编辑距离 加上一个替换元素的操作。

即 dp[i][j] = dp[i - 1][j - 1] + 1;

综上,当 if (word1[i - 1] != word2[j - 1]) 时取最小的,即:dp[i][j] = min({dp[i - 1][j - 1], dp[i - 1][j], dp[i][j - 1]}) + 1;

递归公式代码如下:

if (word1[i - 1] == word2[j - 1]) {
    dp[i][j] = dp[i - 1][j - 1];
}
else {
    dp[i][j] = min({dp[i - 1][j - 1], dp[i - 1][j], dp[i][j - 1]}) + 1;
}






 

标签:代码,随想录,len,range,word1,word2,字符串,动态,dp
From: https://www.cnblogs.com/fangleSea/p/17526992.html

相关文章

  • 代码随想录|动态规划-编辑距离
     392.判断子序列 115.不同的子序列   583. 两个字符串的删除操作  72. 编辑距离 编辑距离总结篇392.判断子序列和昨天的最长重复子串一样,只要计算两者的重复长度是不是和s一样就行了。但是还是不如双指针的时间复杂度O(nm)O(nm)classSolution:de......
  • 10 个 jQuery 动态效果
    Whatevercontentyouhavetopresent,youcanpresenttheminamoreinteractive&moreresponsiveways.Inthisarticlewe’dliketopresent10BrillianttechniquesusingsomejQuerymagictograbtheattentionofyouruserswithasimple,richuser......
  • 将代码和笔记之类的保存到数据库
    平时记录在工作中,会把随手查到的内容,记在文件里面,时间一久,比较零乱,文件太长,在里面查找也不方便。于是想到随便整理一下存数据库得了。先创建数据库,mysql8支持全文索引,自带分词器,用起来很方便。CREATETABLE`books`(`id`intunsignedNOTNULLAUTO_INCREMENT,`title`......
  • 象群游牧优化算法(EHO)(Matlab完整代码实现)
    ......
  • JeecgBoot低代码开发平台与达梦数据完成兼容性互认证
    近日,JeecgBoot与达梦数据库管理系统V8完成兼容性认证测试;通过双方共同测试表明,Jeecgboot低代码开发平台与达梦数据库管理系统V8,相互兼容,系统功能运行稳定,能够满足用户更多的性能需求;并签署产品兼容互认证明。JeecgBoot将持续进行更多的国产化软件及国产化服务器的兼容性测试,将会不......
  • js代码加密,保护js文件刻不容缓
    随着互联网的高速发展,网站运行的javaSCRIPT代码常常被别人轻易的拷贝,因此程序员不得不对想办法保护自的代码---javascript加密。现在网络上面有太多的拿来主义,当然这也是没有办法避免的一种现象,网络的开放性使得一切都没有什么秘密可言,所以代码加密便顺应而产生。js代码加密,保护......
  • 封装$tryCatch方法(axios请求方法),避免写重复代码
    封装$tryCatch方法(axios请求方法),避免写重复代码:https://blog.csdn.net/qq_41995320/article/details/122621498?spm=1001.2101.3001.6650.1&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-122621498-blog-109624790.235%5Ev38%5Epc_relevant......
  • 智能化新服务即将惊艳亮相HDC2023 ——华为云Astro爆发低代码能量
    HDC期间可参与华为开发者大会Astro新人抽奖活动,活动链接在文末。福利多多,快来参与!跃跃欲试的开发者们,是否对2023华为云开发者大会充满期待?华为云Astro将引领新一轮低代码技术革命!贯穿AIGC技术的低代码新服务破茧而出——自然语言生成工作流!这神秘的魔法技能,即将呈现在各位望眼欲穿......
  • java爬虫如何使用动态代理ip
      在进行网络爬虫开发时,使用动态IP代理是保护自己的隐私、绕过访问限制和提高爬虫稳定性的重要技术。下面呢是一个简单的Java爬虫动态IP代理教程,用来帮助大家实现动态切换IP地址。1.寻找可靠的代理服务提供商 在开始之前,您需要找到一个可靠的代理服务提供商,他们将提供动态I......
  • [记]Rust闭包加动态分发
    pubtraitApp{fnrun(&mutself);}#[derive(Clone,Copy)]pubstructCda{d:i32,}implCda{fnnew(num:i32)->Self{Self{d:num}}fninc(&mutself)->Self{self.d+=1;*self}fnshow(......