leetcode刷题详解十二

回文子串

首先回文子串问题涉及到的都是单个字符串,所以如果是单个字符串用动态规划的基本都是二维的,i-j

其次,回文字符串,都是从后往前遍历的,这个要记住。因为dp的状态转移方程特性决定的

647. 回文子串

还是用动态规划吧,中心扩散没太看懂

参考链接

说一下自己的想法,因为就单个字符串,因此我之前说过单个字符串的话是要有范围的。

为什么外循环会从len-1开始,对于字符串“cabac来说”,如果s[0]的c=s[4]的c,那么只需要看“aba”即可,如果你的for循环从0开始的换,你都从0过来了还看什么aba,只有从后往前,才能看aba吧

int countSubstrings(string s) {int len = s.size();vector<vector<bool>> dp(len, vector<bool>(len, false));int count = 0;for(int i = len - 1; i >= 0; i--){for(int j = i; j < len; j++){if(s[i] == s[j] ){if(j-i <= 1){count++;dp[i][j] = true;}else if(dp[i + 1][j - 1]){count++;dp[i][j] = true;}}}}return count;
}
516. 最长回文子序列

没思路看这个

思路:这道题一定要和647放在一起看,这两道题是一模一样的类型。

这道题的难点主要在于dp的状态转移过程,来分析一下

如果s[i]与s[j]相同,那么 d p [ i ] [ j ] = d p [ i + 1 ] [ j − 1 ] + 2 dp[i][j] = dp[i + 1][j - 1] + 2 dp[i][j]=dp[i+1][j1]+2,因为回文串的个数嘛,+2

如果s[i]与s[j]不相同,说明s[i]和s[j]的同时加入 并不能增加[i,j]区间回文子串的长度,那么分别加入s[i]、s[j]看看哪一个可以组成最长的回文子序列。

加入s[j]的回文子序列长度为 d p [ i + 1 ] [ j ] dp[i + 1][j] dp[i+1][j]

加入s[i]的回文子序列长度为 d p [ i ] [ j − 1 ] dp[i][j - 1] dp[i][j1]

那么dp[i][j]一定是取最大的,即: d p [ i ] [ j ] = m a x ( d p [ i + 1 ] [ j ] , d p [ i ] [ j − 1 ] ) dp[i][j] = max(dp[i + 1][j], dp[i][j - 1]) dp[i][j]=max(dp[i+1][j],dp[i][j1])

 int longestPalindromeSubseq(string s) {int len = s.size();vector<vector<int>> dp(len+1, vector<int>(len+1, 0));for(int i = 1; i < len + 1; i++ ){dp[i][i] = 1;}for(int i = len ; i >= 1; i--){//这里j从i+1开始,和上一题不一样,因为不考虑本身for(int j = i + 1; j < len+1; j++){if(s[i-1] == s[j-1]){dp[i][j] = dp[i+1][j-1] + 2;}else{//不构成回文串了,不能+1了dp[i][j] = max(dp[i][j-1], dp[i+1][j]);}}}return dp[1][len];}
1312. 让字符串成为回文串的最少插入次数

其他应用题

另类的DP套路!

887. 鸡蛋掉落

参考链接

最重要的是下面的这张图:

图片
  • 注意事项,一定要注意两层是 d p [ i ] [ j ] = a dp[i][j]=a dp[i][j]=a,三层的话肯定是最里面一层for有个变量temp,然后跳开里面这层才是dp赋值
int superEggDrop(int k, int n) {//dp数组的含义是到第n层有k个鸡蛋可以进行的最小的操作次数vector<vector<int>> dp(n+1, vector<int>(k+1, 0));for(int i = 1; i < k + 1; i++){dp[1][i] = 1;}//一个鸡蛋扔肯定每楼扔一次for(int i = 1; i < n + 1; i++){dp[i][1] = i;}for(int i = 2; i < n + 1; i++){for(int j = 2;j < k + 1; j++){//楼层区间int temp = INT_MAX;for(int m = 1; m <= i; m++){//最坏就是最大temp = min(temp, max(dp[m-1][j-1], dp[i-m][j])+1);}dp[i][j] = temp;}}return dp[n][k];}

这样子写会超时!不过重要的是思路

  • 第二种思路!!!!

    其实第二种思路最主要的就是对于dp数组的设计了,dp设计好了一道题也就自然而然的解开了

    我们把dp设计成 d p [ i ] [ j ] dp[i][j] dp[i][j]表示有i个鸡蛋,走了m步能到的层数,因此 d p [ i ] [ j ] dp[i][j] dp[i][j]就是层数,只要层数大于等于n,就可以返回j

    状态转移方程这样理解:

    当我们扔鸡蛋的时候,都是两种情况,碎或者不碎,不管碎没碎,都用掉了一步(+1),

    无论你在哪层楼扔鸡蛋,鸡蛋只可能摔碎或者没摔碎,碎了的话就测楼下,没碎的话就测楼上

    无论你上楼还是下楼,总的楼层数 = 楼上的楼层数 + 楼下的楼层数 + 1(当前这层楼)

    因此 d p [ i ] [ j ] dp[i][j] dp[i][j]的i是次数,不管碎没碎总是i-1!!

    至于为啥初始化dp数组的时候要用k+1和n+1是因为最多次数不可能超过楼层数吧!!!

    int superEggDrop(int k, int n) {vector<vector<int>> dp(n+1, vector<int>(k+1, 0));for(int i = 1; i < n + 1; i++){for(int j = 1; j < k + 1; j++){dp[i][j] = dp[i-1][j]/*鸡蛋没碎,注意这里面i是次数!*/ + dp[i-1][j-1]/*鸡蛋碎了*/ + 1;if(dp[i][j] >= n){return i;}}}return n;}
    
312. 戳气球
  • 思路分析,其实这道题dp应该这样定义,即 d p [ i ] [ j ] dp[i][j] dp[i][j]指的是区间i到j中,所得到的气球的最大值。

    那么经过前面的洗礼,我们很自然而然的就会想到在i和j之间用一个参数k来分割,因此自然而然是三个for循环,然后

    d p [ i ] [ j ] = m a x ( d p [ i ] [ j ] , 状态转移 ) dp[i][j] = max(dp[i][j], 状态转移) dp[i][j]=max(dp[i][j],状态转移)

    很自然而然

    但是这道题很经典在于,k这个气球是最后一个被戳爆的,一定要记住!!!

    根据状态转移方程可以画一下图,看看求 d p [ i ] [ j ] dp[i][j] dp[i][j]需要先求那一行一列,很清楚明白

    参考链接

int maxCoins(vector<int>& nums) {int n = nums.size();vector<int> temp_nums(n+2);temp_nums[0] = 1;for(int i = 0; i < n ; i++){temp_nums[i+1] = nums[i];}temp_nums[n+1] = 1;vector<vector<int>> dp(n+2, vector<int>(n+2, 0));for(int i = n; i >= 0; i--){for(int j = i + 1; j < n + 2; j++){for(int k = i + 1; k < j; k++){dp[i][j] = max(dp[i][j], dp[i][k]+dp[k][j]+(temp_nums[k]* temp_nums[i]* temp_nums[j]));}}}return dp[0][n+1];}

贪心算法

<思想>

寻找最优解问题,一般将求解过程分成若干个步骤,每个步骤都应用贪心原则,当前(局部)最优的选择,从局部最优策略扩展到全局的最优解。基本步骤如下:

  1. 从某个初始解出发
  2. 采用迭代的过程,当可以向目标前进一步时,根据局部最优策略,得到一部分解然后缩小问题规模
  3. 将所有解综合起来。

<简单题>

455. 分发饼干
  • 简单的贪心算法,排序+双指针
int findContentChildren(vector<int>& g, vector<int>& s) {sort(g.begin(), g.end());sort(s.begin(), s.end());cout<<g[0]<<endl;int i = 0;int j = 0;int g_size = g.size();int s_size = s.size();int count = 0;while(i < g_size && j <s_size){if(g[i] <= s[j]){i++;j++;count++;}else{j++;}}return count;}
1005. K 次取反后最大化的数组和
  • 刚开始想着从小到大排序然后把小的变成负的就行,但是这样是有问题的,因为没有考虑负数的问题。

    因此思路变成,计算一个数组中负数的个数,然后和k次比较,如果k大于负数的个数m,则将所有负数变成整数然后重新排序。

    如果k小于负数的个数m,则最小的负数变成正数就行

int largestSumAfterKNegations(vector<int>& nums, int k) {int neg_num = 0;int len = nums.size();int sum = 0;for(int i = 0; i < len; i++){if(nums[i] < 0){neg_num++;}}sort(nums.begin(), nums.end());if(k > neg_num){//先把负数变成正数for(int i = 0; i < neg_num; i++){nums[i] = -nums[i];}k = (k - neg_num) % 2;//接下来全部变成正数数组sort(nums.begin(), nums.end());for(int i = 0; i < k; i++){nums[i] = -nums[i];}for(int i = 0; i < len; i++){sum+=nums[i];}}else{for(int i = 0; i < k; i++){nums[i] = -nums[i];}for(int i = 0; i < len; i++){sum+=nums[i];}}return sum;}
860. 柠檬水找零
bool lemonadeChange(vector<int>& bills) {int len = bills.size();if(bills[0] != 5){return false;}//注意只有5,10,20的面值int five = 0;int ten = 0;int twenty = 0;int temp = 0;for(int i = 0; i < len; i++){if(bills[i] == 5){five++;}else if(bills[i] == 10){ten++;}else if(bills[i] == 20){twenty++;}temp = bills[i] - 5;if(temp == 5){five--;}else if(temp == 15){//更倾向于10+5这种方式找零if(ten > 0 ){ten--;five--;}else{       five = five - 3;}}if(five < 0 || ten < 0 || twenty <0){return false;}}return true;}

<中等偏上>

376. ❤摆动序列
  • 贪心算法: 局部最优然后达到全局最优

    这道题想到错了这么多次

    首先要注意,峰值最右边的永远有1个,因此count初值=1,但是这道题为什么卡这么久,因为首先越界不报错,我真是服了,第二就是判定条件,一定要看仔细!!!!

int wiggleMaxLength(vector<int>& nums) {//峰值法int pre = 0;int now = 0;int n = nums.size();if(n <= 1){return n;}int count = 1;for(int i = 0; i < n-1 ; i++){now = nums[i+1] - nums[i];if((pre <= 0 && now > 0) || (pre >= 0 && now < 0)){count++;pre = now;}}return count;}
738. 单调递增的数字
  • 思路:如果说是暴力破解的话,肯定是不可以的。

    可以考虑局部最优,单调递增就意味着最后一位最大为9。

    参考链接

    本来我没有加flag标志位,代码如下:

    int monotoneIncreasingDigits(int n) {string str_num = to_string(n);int len = str_num.size();if(n < 10){return n;}for(int i = len - 1; i > 0; i--){if(str_num[i] < str_num[i-1]){cout<<"str_num[i]:"<<str_num[i]<<endl;cout<<"str_num[i-1]:"<<str_num[i-1]<<endl;str_num[i] = '9';cout<<"str_num[i]:"<<str_num[i]<<endl;cout<<"str_num[i-1]:"<<str_num[i-1]<<endl;str_num[i-1]--;}}return stoi(str_num);
    

    这样写是错误的,遇见100这个用例就知道了,进入if的时候i为1,因此要加一个flag标志位,从该标志为往后都设置为9!

代码如下:

int monotoneIncreasingDigits(int n) {string str_num = to_string(n);int len = str_num.size();if(n < 10){return n;}int flag = len;for(int i = len - 1; i > 0; i--){if(str_num[i] < str_num[i-1]){flag = i;str_num[i-1]--; }}for(int i = flag; i < len; i++){str_num[i] = '9';}return stoi(str_num);}
135. 分发糖果
  • 思路

    “相邻的孩子中评分高的孩子必须获得更多的糖果”这句话拆分成为了两个规则:

    1. 从数组左边开始遍历,当ratings[i] > rating[i-1]时,则必须保证第i个孩子的糖果比第i-1个的多

      这个是后比较是不完整的,比如说[1,0,2]这个数组,只比较了0,12,0,对于0,21,0这个顺序没有对比

    2. 因此还要从数组的右边开始遍历,比对一次,当ratings[i] > ratings[i]+1的时候,保证第i个孩子的糖果比第i+1个的多

    因此加入有个数组时[1,0,2],左边开始遍历得到数组[1,1,2],右边开始遍历的到数组[2,1,1],对于两个数组的同一个索引取最大值,即2+1+2=5

  • 代码

    int candy(vector<int>& ratings) {int n = ratings.size();if(n <= 1){return 1;}vector<int> left(n, 1);vector<int> right(n, 1);for(int i = 1; i < n; i++){if(ratings[i] > ratings[i - 1]){left[i] = left[i - 1] + 1;}}for(int i = n - 2; i >= 0; i-- ){if(ratings[i] > ratings[i + 1]){right[i] = right[i + 1] + 1;}}int candy_num = 0;for(int i = 0; i < n; i++){candy_num +=max(left[i], right[i]);}return candy_num;}
    
406. 根据身高重建队列
  • 思路:

    这道题就是排序,让数组变得有意义起来

    思路很简单,首先根据第一个值倒序排序,因为第二个值的含义是前面有多少个大于等于第一个值的人,因此我们肯定先倒序。然后看第二个值,依次遍历数组,第二个值和索引比较,大于等于索引的就push_back,小于的就insert。

  • 代码:

    //加static是因为my_function函数其实有三个形参,第三个是this指针,但是sort中只用到了两个参数,参数不匹配//所以要加static,因为static成员函数没有this指针static bool my_function(vector<int>& vec1, vector<int>& vec2){return vec1[0] > vec2[0] || (vec1[0] == vec2[0] && vec1[1] < vec2[1]);}vector<vector<int>> reconstructQueue(vector<vector<int>>& people) {sort(people.begin(), people.end(), my_function);vector<vector<int>> temp;for(int i = 0; i < people.size(); i++){if(people[i][1] >= i){temp.push_back(people[i]);}else{temp.insert(temp.begin() + people[i][1], people[i]);}}return temp;}
    
  • 补充

    当数组时(key,value)类似类型的时候如何比较大小?我写到了代码集合里面

55. 跳跃游戏
  • 思路

    其实这道题应该换个问法,即通过下面数组的跳跃规则,最多可以跳多远?这样如果跳出去最远超过了数组长度,直接返回true就好了,小于数组长度说明跳不到最后一个格子。

  • 代码

    bool canJump(vector<int>& nums) {int jump_to_index = 0;int n = nums.size();for(int i = 0; i < n  ; i++){if(i > jump_to_index){return false;}jump_to_index = max(jump_to_index, i + nums[i]);}return true;
    }
    

    首先我觉得这个题用“能跳跃到的索引”表示是最好的,因为 i + nums[i]指的就是能够跳跃到的最大的索引。

    有了上面这个理解下面就好理解很多,之前代码错就是将for里面的判断语句放到了下面,其实应该先判断在计算,先判断就表明对于下一个索引,jump_to_index能否到达。

45. 跳跃游戏 II
  • 思路

    贪婪贪婪,选择一个能调的最远的往下走,肯定就是最小值了

  • 代码

     int jump(vector<int>& nums) {int jump_to_index = 0;int end_index = 0;int step = 0;int n = nums.size();for(int i = 0; i < n-1; i++){jump_to_index = max(jump_to_index, i+nums[i]);if(i == end_index){step++;end_index = jump_to_index;}}return step;}
    

    i < n-1是因为最后一次到达最后一个位置就不用再跳跃了。

    为什么是i == end_index,因为题目上说了“假设你总是可以到达数组的最后一个位置”,因此我总能到达最后一个位置,不用考虑越界的情况

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

sizeof 和 strlen的对比及笔试题目

目录 题目1&#xff1a; 题目2&#xff1a; 题目3&#xff1a; 题目4&#xff1a; 题目5&#xff1a; 题目6&#xff1a; 二维数组题&#xff08;重点&#xff09;&#xff1a; 上述题目总结&#xff1a; 若想了解其他的字符函数和字符串函数请移步&#xff1a;深入理解…

金色传说:SAP-BC-SM36/SM37后台执行报表查询结果导出时分页和标题多行问题解决

文章目录 前言一、后台执行的启动方式?二、使用步骤1.点击程序-后台执行2.结果查看和导出三、结果显示和导出问题1.结果显示不全问题解决2.结果如何导出到Excel中3.==导出的文件有分页和标题和结果都是多行显示问题解决==总结前言 例如:随着项目数据量增长,很多报表的查询时…

Django连接数据库

连接数据库 接下来讲的几个步骤不需要区分先后&#xff0c;但都得进行操作 settings.py的操作 #!settings.py ....别的代码DATABASES {default: { ENGINE: django.db.backends.mysql, NAME: day1121, # 数据库名字&#xff0c;要先创建好 USER: root, PASSWORD: 123456, HO…

HarmonyOS——UI开展前的阶段总结

当足够的了解了HarmonyOS的相关特性之后&#xff0c;再去介入UI&#xff0c;你会发现无比的轻松&#xff0c;特别当你有着其他的声明式UI开发的经验时&#xff0c;对于HarmonyOS的UI&#xff0c;大致一扫&#xff0c;也就会了。 如何把UI阐述的简单易懂&#xff0c;又能方便大…

hyper-V操作虚拟机ubuntu 22.03

安装hyper-V 点击卸载程序 都勾选上即可 新建虚拟机&#xff0c;选择镜像文件 选择第一代即可 设置内存 配置网络 双击 启动安装虚拟机 输入用户名 zenglg 密码&#xff1a;LuoShuwen123456 按照enter键选中openssh安装 安装中 安装完成 选择重启 输入用户名、密码

20.Oracle11g中的触发器

oracle11g中的触发器 一、触发器的概述1、什么是触发器2、触发器的类型3、触发器的组成4、触发器的作用 二、触发器的创建语法1、创建语法2、数据库启动触发器3、 用户登录触发器&#xff1a; 三、对触发器的基本操作点击此处跳转下一节&#xff1a;21.Oracle的程序包(Package)…

redis的过期策略以及定时器的实现

Redis是客户端服务器结构的程序&#xff0c;客户端与服务器通过网络通信&#xff0c;所以对于keys *这种的操作在大型企业中不太建议&#xff0c;生产环境下的key会非常多&#xff0c;Redis是但现成的服务器&#xff0c;执行keys*的时间非常长&#xff0c;就会导致redis服务器阻…

【LeetCode】101. 对称二叉树

101. 对称二叉树 难度&#xff1a;简单 题目 给你一个二叉树的根节点 root &#xff0c; 检查它是否轴对称。 示例 1&#xff1a; 输入&#xff1a;root [1,2,2,3,4,4,3] 输出&#xff1a;true示例 2&#xff1a; 输入&#xff1a;root [1,2,2,null,3,null,3] 输出&#…

token认证机制,基于JWT的Token认证机制实现,安全性的问题

文章目录 token认证机制几种常用的认证机制HTTP Basic AuthOAuthCookie AuthToken AuthToken Auth的优点 基于JWT的Token认证机制实现JWT的组成认证过程登录请求认证 对Token认证的五点认识JWT的JAVA实现 基于JWT的Token认证的安全问题确保验证过程的安全性如何防范XSS Attacks…

Redis 基础、字符串、哈希、有序集合、集合、列表以及与 Jedis 操作 Redis 和与 Spring 集成。

目录 1. 数据类型 1.1 字符串 1.2 hash 1.3 List 1.4 Set 1.5 sorted set 2. jedis操作redis 3. 与spring集成 1. 数据类型 1.1 字符串 String是最常用的数据格式&#xff0c;普通的kay-value都归结为此类&#xff0c; value值不仅可以是string&#xff0c;可以是数字…

interface previously declared 的bug问题

其实就是重复定义了&#xff0c;只需要加如下的代码即可&#xff1a; 其中把APB的部分改成自己的接口名字就好了。

JS 绘制半径不一致的环形图进度条

HTML部分: <canvas id"mycanvas" width"100" height"100"></canvas>JS部分&#xff1a; const option {element: "mycanvas", // 元素count: 26, // 高亮数据totalCount: 129, // 总数据progressColor: #3266FB, // 进…