力扣练习题(2024/4/19)

1两个字符串的删除操作

给定两个单词 word1 和 word2 ,返回使得 word1 和  word2 相同所需的最小步数

每步 可以删除任意一个字符串中的一个字符。

示例 1:

输入: word1 = "sea", word2 = "eat"
输出: 2
解释: 第一步将 "sea" 变为 "ea" ,第二步将 "eat "变为 "ea"

示例  2:

输入:word1 = "leetcode", word2 = "etco"
输出:4

提示:

  • 1 <= word1.length, word2.length <= 500
  • word1 和 word2 只包含小写英文字母

动态规划思路:

  1. 定义 dp[i][j] 为以第 i-1 个字符为结尾的字符串 word1,和以第 j-1 个字符为结尾的字符串 word2,想要达到相等,所需要删除元素的最少次数。

  2. 初始化 dp 数组:

    • 当 word2 为空字符串时,word1 的任意子串都需要删除所有字符才能与之相等,因此 dp[i][0] = i
    • 当 word1 为空字符串时,无论 word2 如何,都不需要删除任何字符,因此 dp[0][j] = 0。这一步在代码中被默认初始化为 0。
  3. 状态转移方程:

    • 当 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 个字符与 word2 的第 j-1 个字符不相等时,此时需要删除字符,dp[i][j] 可以由以下几种操作得到:
      1. 删除 word1 的第 i-1 个字符,转化为以 word1 的前 i-2 个字符与 word2 的前 j-1 个字符相等的状态,操作数加一,即 dp[i][j] = dp[i - 1][j] + 1
      2. 删除 word2 的第 j-1 个字符,转化为以 word1 的前 i-1 个字符与 word2 的前 j-2 个字符相等的状态,操作数加一,即 dp[i][j] = dp[i][j - 1] + 1
      3. 同时删除 word1 的第 i-1 个字符和 word2 的第 j-1 个字符,转化为以 word1 的前 i-2 个字符与 word2 的前 j-2 个字符相等的状态,操作数加二,即 dp[i][j] = dp[i - 1][j - 1] + 2
  4. 最终返回 dp[word1.size()][word2.size()],即以 word1 和 word2 为结尾的字符串相等所需要删除的最少次数。

代码:

class Solution {
public:
    int minDistance(string word1, string word2) {
        // 创建二维数组 dp,dp[i][j] 表示将 word1 的前 i 个字符转换为 word2 的前 j 个字符所需的最小操作数
        vector<vector<int>> dp(word1.size() + 1, vector<int>(word2.size() + 1));
        
        // 初始化 dp 数组:当 word2 为空字符串时,需要删除 word1 的所有字符;当 word1 为空字符串时,不需要删除任何字符
        for (int i = 0; i <= word1.size(); i++) dp[i][0] = i;
        for (int j = 0; j <= word2.size(); j++) dp[0][j] = j;
        
        // 动态规划计算最小操作数
        for (int i = 1; i <= word1.size(); i++) {
            for (int j = 1; j <= word2.size(); j++) {
                if (word1[i - 1] == word2[j - 1]) {
                    // 当前字符相等,不需要额外操作,继承前一个状态的操作数
                    dp[i][j] = dp[i - 1][j - 1];
                } else {
                    // 当前字符不相等,选择删除 word1 的第 i 个字符或者删除 word2 的第 j 个字符中操作数较小的情况
                    dp[i][j] = min(dp[i - 1][j] + 1, dp[i][j - 1] + 1);
                }
            }
        }
        
        // 返回将 word1 转换为 word2 所需的最小操作数
        return dp[word1.size()][word2.size()];
    }
};

2 编辑距离

给你两个单词 word1 和 word2, 请返回将 word1 转换成 word2 所使用的最少操作数  。

你可以对一个单词进行如下三种操作:

  • 插入一个字符
  • 删除一个字符
  • 替换一个字符

示例 1:

输入:word1 = "horse", word2 = "ros"
输出:3
解释:
horse -> rorse (将 'h' 替换为 'r')
rorse -> rose (删除 'r')
rose -> ros (删除 'e')

示例 2:

输入:word1 = "intention", word2 = "execution"
输出:5
解释:
intention -> inention (删除 't')
inention -> enention (将 'i' 替换为 'e')
enention -> exention (将 'n' 替换为 'x')
exention -> exection (将 'n' 替换为 'c')
exection -> execution (插入 'u')

提示:

  • 0 <= word1.length, word2.length <= 500
  • word1 和 word2 由小写英文字母组成

思路:

 1. 定义 `dp[i][j]` 表示以下标 i-1 为结尾的字符串 `word1`,和以下标 j-1 为结尾的字符串 `word2`,最近编辑距离为 `dp[i][j]`。

2. 初始化二维数组 `dp`: - 初始化 `dp[i][0] = i`,表示当 `word2` 为空字符串时,将 `word1` 的前 `i` 个字符全部删除即可,操作次数为 `i`。 - 初始化 `dp[0][j] = j`,表示当 `word1` 为空字符串时,将 `word2` 的前 `j` 个字符全部插入即可,操作次数为 `j`。

3. 动态规划求解最小编辑距离: - 对于每个位置 `(i, j)`,如果 `word1[i - 1]` 等于 `word2[j - 1]`,说明当前字符相同,不需要进行操作,取左上角的值 `dp[i - 1][j - 1]`; - 如果当前字符不同,可以选择插入、删除、替换操作中的最小值,并加上一次操作: - `dp[i][j] = min(dp[i - 1][j - 1], dp[i - 1][j], dp[i][j - 1]) + 1`。

4. 最终返回 `dp[word1.size()][word2.size()]`,即将 `word1` 转换成 `word2` 所需的最小操作次数。 这种动态规划的方法利用填表法,逐步计算出最小编辑距离,从而实现字符串之间的匹配和转换。

代码:

class Solution {
public:
    int minDistance(string word1, string word2) {
        // 创建二维数组 dp,dp[i][j] 表示将 word1 的前 i 个字符转换成 word2 的前 j 个字符所需的最小操作次数
        vector<vector<int>> dp(word1.size() + 1, vector<int>(word2.size() + 1, 0));
        
        // 初始化 dp 数组的第一行和第一列
        for(int i = 0; i <= word1.size(); i++) 
            dp[i][0] = i; // word2 为空字符串时,将 word1 的前 i 个字符全部删除即可
        for(int j = 0; j <= word2.size(); j++)  
            dp[0][j] = j; // word1 为空字符串时,将 word2 的前 j 个字符全部插入即可
        
        // 动态规划求解最小编辑距离
        for(int i = 1; i <= word1.size(); i++) {
            for(int j = 1; j <= word2.size(); j++) {
                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;
                }
            }
        }
        
        // 返回最小编辑距离
        return dp[word1.size()][word2.size()];
    }
};

3回文子串

给你一个字符串 s ,请你统计并返回这个字符串中 回文子串 的数目。

回文字符串 是正着读和倒过来读一样的字符串。

子字符串 是字符串中的由连续字符组成的一个序列。

具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。

示例 1:

输入:s = "abc"
输出:3
解释:三个回文子串: "a", "b", "c"

示例 2:

输入:s = "aaa"
输出:6
解释:6个回文子串: "a", "a", "a", "aa", "aa", "aaa"

提示:

  • 1 <= s.length <= 1000
  • s 由小写英文字母组成

思路:

首先,我们定义一个二维数组 dp,其中 dp[i][j] 表示字符串 s 中以索引 i 开始、索引 j 结束的子串是否为回文串。初始化时,我们将所有 dp[i][j] 的值设为 false,表示初始时所有子串都不是回文串。

然后,我们从字符串的末尾开始向前遍历,这样我们可以先计算出小区间的结果,再利用小区间的结果计算大区间的结果。对于每个索引 i,我们再从 i 开始向后遍历,对于每个索引 j,我们判断 s[i] 是否等于 s[j]

  • 如果 s[i] 等于 s[j],则可能存在回文子串。我们进一步分析:
    1. 如果 j - i 小于等于 1,说明子串长度为 1 或 2,此时 s[i] 和 s[j] 相等,所以子串是回文串,我们将结果加一,并将 dp[i][j] 设为 true
    2. 如果 j - i 大于 1,且 dp[i + 1][j - 1] 为 true,说明内部子串是回文串,且 s[i] 和 s[j] 相等,此时整个子串也是回文串,我们将结果加一,并将 dp[i][j] 设为 true

最终,我们返回计算得到的回文子串的数量。

代码

class Solution {
public:
    int countSubstrings(string s) {
        // 创建二维数组 dp,dp[i][j] 表示 s 中以索引 i 开始、索引 j 结束的子串是否为回文串
        vector<vector<bool>> dp(s.size(), vector<bool>(s.size(), false));
        int result = 0;
        
        // 从后向前遍历字符串 s,保证先计算出小区间的结果,再利用小区间的结果计算大区间的结果
        for (int i = s.size() - 1; i >= 0; i--) {
            for (int j = i; j < s.size(); j++) {
                if (s[i] == s[j]) {
                    if (j - i <= 1) { // 情况一:子串长度为 1 或 2
                        result++; // 单个字符或相邻字符构成的子串都是回文串
                        dp[i][j] = true;
                    } else if (dp[i + 1][j - 1]) { // 情况二:子串长度大于 2,且内部子串是回文串
                        result++; // 内部子串是回文串,且两端字符相同,则整个子串也是回文串
                        dp[i][j] = true;
                    }
                }
            }
        }
        
        // 返回回文子串的数量
        return result;
    }
};

4. 最长回文子序列

给你一个字符串 s ,找出其中最长的回文子序列,并返回该序列的长度。

子序列定义为:不改变剩余字符顺序的情况下,删除某些字符或者不删除任何字符形成的一个序列。

示例 1:

输入:s = "bbbab"
输出:4
解释:一个可能的最长回文子序列为 "bbbb" 。

示例 2:

输入:s = "cbbd"
输出:2
解释:一个可能的最长回文子序列为 "bb" 。

提示:

  • 1 <= s.length <= 1000
  • s 仅由小写英文字母组成

思路:

  1. 定义状态: 首先,我们需要定义动态规划状态。在这里,我们使用二维数组 dp[i][j],其中 dp[i][j] 表示从字符串 s 的下标 i 到 j 之间的子串中的最长回文子序列长度。

  2. 状态转移方程: 接下来,我们需要找到状态之间的转移关系。当我们在考虑 dp[i][j] 时,我们可以根据 s[i] 和 s[j] 的关系来决定如何更新 dp[i][j] 的值:

    • 如果 s[i] == s[j],那么 dp[i][j] 可以由 dp[i + 1][j - 1] 推导而来,即 dp[i][j] = dp[i + 1][j - 1] + 2,因为两端相同的字符可以增加最长回文子序列的长度。
    • 如果 s[i] != s[j],那么 dp[i][j] 可以由 dp[i + 1][j] 和 dp[i][j - 1] 中的较大值推导而来,因为当前位置的字符不同,所以无法直接形成回文,只能取相邻子串中的最长回文子序列的长度作为当前子串的最长回文子序列长度,即 dp[i][j] = max(dp[i + 1][j], dp[i][j - 1])
  3. 初始化: 我们需要对动态规划数组进行初始化。对角线上的元素 dp[i][i] 表示单个字符的子串为回文子序列,因此初始化为 1。

  4. 状态转移计算: 我们从字符串的末尾开始向前遍历,逐步计算出每个子串的最长回文子序列长度,直到计算出整个字符串的最长回文子序列长度。

  5. 返回结果: 最后,我们返回 dp[0][s.size() - 1],即整个字符串的最长回文子序列长度

代码:

class Solution {
public:
    int longestPalindromeSubseq(string s) {
        // 创建二维数组 dp,dp[i][j] 表示以下标 i 到 j 之间的子串中的最长回文子序列长度
        vector<vector<int>> dp(s.size(), vector<int>(s.size(), 0));
        
        // 初始化 dp 数组的对角线,即单个字符的子串为回文子序列,长度为 1
        for (int i = 0; i < s.size(); i++) 
            dp[i][i] = 1;
        
        // 动态规划求解最长回文子序列长度
        for (int i = s.size() - 1; i >= 0; i--) {
            for (int j = i + 1; j < s.size(); j++) {
                if (s[i] == s[j]) {
                    dp[i][j] = dp[i + 1][j - 1] + 2; // 当前字符相同,加上两端字符的最长回文子序列长度
                } else {
                    dp[i][j] = max(dp[i + 1][j], dp[i][j - 1]); // 当前字符不同,取相邻子串中的最长回文子序列长度
                }
            }
        }
        
        // 返回整个字符串的最长回文子序列长度
        return dp[0][s.size() - 1];
    }
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/558887.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ProgressFlowmon的confluence接口存在任意命令执行漏洞(CVE-2024-2389)

声明&#xff1a; 本文仅用于技术交流&#xff0c;请勿用于非法用途 由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;文章作者不为此承担任何责任。 简介 ProgressFlowmon是一整套用于网络映射、应用程序性能…

客户端动态降级系统

本文字数&#xff1a;4576字 预计阅读时间&#xff1a;20分钟 01 背景 无论是iOS还是Android系统的设备&#xff0c;在线上运行时受硬件、网络环境、代码质量等多方面因素影响&#xff0c;可能会导致性能问题&#xff0c;这一类问题有些在开发阶段是发现不了的。如何在线上始终…

大气的免费wordpress模板

国产的wordpress模板&#xff0c;更适合中国人使用习惯&#xff0c;更符合中国老板的审美的大气wordpress企业官网建站模板。 WordPress模板&#xff0c;也称为主题&#xff0c;是用于定义WordPress网站或博客外观和功能的预设计文件集。这些模板使用HTML、CSS和PHP代码构建&a…

上传文件到HDFS

1.创建文件夹 hdfs -dfs -mkdir -p /opt/mydoc 2.查看创建的文件夹 hdfs -dfs -ls /opt 注意改文件夹是创建在hdfs中的&#xff0c;不是本地&#xff0c;查看本地/opt&#xff0c;并没有该文件夹。 3.上传文件 hdfs dfs -put -f file:///usr/local/testspark.txt hdfs://m…

【深度学习】Vision Transformer

一、Vision Transformer Vision Transformer (ViT)将Transformer应用在了CV领域。在学习它之前&#xff0c;需要了解ResNet、LayerNorm、Multi-Head Self-Attention。 ViT的结构图如下&#xff1a; 如图所示&#xff0c;ViT主要包括Embedding、Encoder、Head三大部分。Class …

Docker in Docker的原理与实战

Docker in Docker&#xff08;简称DinD&#xff09;是一种在Docker容器内部运行另一个Docker实例的技术。这种技术允许用户在一个隔离的Docker容器中创建、管理和运行其他Docker容器&#xff0c;从而提供了更灵活和可控的部署选项。以下是DinD的主要特点&#xff1a; 隔离性&am…

力扣打卡第一天

101. 对称二叉树 C&#xff1a; class Solution { public:bool isSymmetric(TreeNode* root) {return check(root->left,root->right);}bool check(TreeNode *p,TreeNode *q){ /**定义check方法用来检查两棵树是否是镜像的*/if (!p && !q) return true; /* 如…

基于SSM的物流快递管理系统(含源码+sql+视频导入教程+文档+PPT)

&#x1f449;文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1 、功能描述 基于SSM的物流快递管理系统2拥有三个角色&#xff1a; 管理员&#xff1a;用户管理、管理员管理、新闻公告管理、留言管理、取件预约管理、收件管理、货物分类管理、发件信息管理等 用户…

如何安全、高速、有效地利用IP代理爬取数据

陈老老老板&#x1f9d9;‍♂️ &#x1f46e;‍♂️本文专栏&#xff1a;生活&#xff08;主要讲一下自己生活相关的内容&#xff09;生活就像海洋,只有意志坚强的人,才能到达彼岸。 &#x1f934;本文简述&#xff1a;如何安全、高速、有效地利用IP代理爬取数据 &#x1f473…

【数据结构-串-数组-广义表】

目录 1 串-理解1.1 串的抽象定义&#xff1a;-理解1.2 串的存储结构-不断掌握1.2.1 顺序存储结构&#xff1a;1.2.2 链式存储结构&#xff1a; 1.3 串的模式匹配算法&#xff1a;-掌握1.3.1 BF暴力求解算法-代码 -掌握1.3.2 KMP求解算法-代码--掌握 2 数组-不断掌握2.1 顺序存储…

WWW ‘24 | EarnMore: 如何利用强化学习来处理可定制股票池中的投资组合管理问题

WWW 24 | EarnMore: 如何利用强化学习来处理可定制股票池中的投资组合管理问题 原创 QuantML QuantML 2024-04-16 09:04 上海 Content 本文主要探讨了如何利用强化学习&#xff08;Reinforcement Learning, RL&#xff09;来处理可定制股票池&#xff08;Customizable Stock …

Golang | Leetcode Golang题解之第40题组合总和II

题目&#xff1a; 题解&#xff1a; func combinationSum2(candidates []int, target int) (ans [][]int) {sort.Ints(candidates)var freq [][2]intfor _, num : range candidates {if freq nil || num ! freq[len(freq)-1][0] {freq append(freq, [2]int{num, 1})} else {…

LabVIEW卡尔曼滤波技术

LabVIEW卡尔曼滤波技术 在现代航空导航中&#xff0c;高精度和快速响应的方位解算对于航空安全至关重要。通过LabVIEW平台实现一种卡尔曼滤波方位解算修正技术&#xff0c;以改善传统导航设备在方位解算中的噪声干扰问题&#xff0c;从而提高其解算精度和效率。通过LabVIEW的强…

Ubuntu上阅读Android源码工具

由于Android源码过于庞杂&#xff0c;里面有多种语言源文件&#xff0c;想只用一IDE统一索引是不现实的。我个人便使用AS阅读JAVA代码&#xff0c;VS看C/C代码&#xff0c;在Ubuntu上不能使用SI&#xff0c;所以直接放弃。在framework开发这个层面上来讲&#xff0c;因为大部分…

Ansible组件说明

1.Ansible Inventory 工作当中有不同的业务主机&#xff0c;我们需要在把这些机器信息存放在inventory里面&#xff0c;ansible默认的inventory的文件是/etc/ansible/hosts&#xff0c;也可以通过ANSIBLE_HOSTS环境变量来指定或者运行ansible和ansible-playbook的时候用-i参数临…

数据可视化(五):Pandas高级统计——函数映射、数据结构、分组聚合等问题解决,能否成为你的工作备用锦囊?

Tips&#xff1a;"分享是快乐的源泉&#x1f4a7;&#xff0c;在我的博客里&#xff0c;不仅有知识的海洋&#x1f30a;&#xff0c;还有满满的正能量加持&#x1f4aa;&#xff0c;快来和我一起分享这份快乐吧&#x1f60a;&#xff01; 喜欢我的博客的话&#xff0c;记得…

js中let和var的区别

在JavaScript中&#xff0c;var、let和const都用于声明变量&#xff0c;但它们之间存在一些重要的区别。特别是let和var之间的区别&#xff0c;我们可以概括为以下几点&#xff1a; 作用域&#xff08;Scope&#xff09;&#xff1a;var有函数作用域或全局作用域&#xff0c;而…

B-树 B+树与数据库原理

B树 引入 概念和性质 插入分析 总结 B树 B*树&#xff08;了解&#xff09; 数据库原理 数据库与B树的关系

【MySQL 数据宝典】【磁盘结构】- 003 双写缓冲区

一、双写缓冲区 ( Doublewrite Buffer Files) 1.1 背景介绍 写失效 (部分页失效) InnoDB的页和操作系统的页大小不一致&#xff0c;InnoDB页大小一般为16K&#xff0c;操作系统页大小为4K&#xff0c;InnoDB的页写入到磁盘时&#xff0c;一个页需要分4次写。如果存储引擎正在…

算法训练营day15

一、层序遍历 参考链接7.2 二叉树遍历 - Hello 算法 (hello-algo.com) 层序遍历本质上属于广度优先遍历&#xff0c;也称广度优先搜索&#xff0c; BFS通常借助队列的先入先出的特性实现 参考链接102. 二叉树的层序遍历 - 力扣&#xff08;LeetCode&#xff09; 像这种较为…
最新文章