【LeetCode算法系列题解】第6~10题

2023-11-11

LeetCode 6. N 字形变换(中等)

【题目描述】

将一个给定字符串 s 根据给定的行数 numRows,以从上往下、从左到右进行 Z 字形排列。
比如输入字符串为 "PAYPALISHIRING" 行数为 3 时,排列如下:

P   A   H   N
A P L S I I G
Y   I   R

之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:"PAHNAPLSIIGYIR"
请你实现这个将字符串进行指定行数变换的函数:

string convert(string s, int numRows);

【示例1】

输入:s = "PAYPALISHIRING", numRows = 3
输出:"PAHNAPLSIIGYIR"

【示例2】

输入:s = "PAYPALISHIRING", numRows = 4
输出:"PINALSIGYAHRPI"
解释:
P     I    N
A   L S  I G
Y A   H R
P     I

【示例3】

输入:s = "A", numRows = 1
输出:"A"

【提示】

1 ≤ s . l e n g t h ≤ 1000 1\le s.length\le 1000 1s.length1000
1 ≤ n u m R o w s ≤ 1000 1\le numRows\le 1000 1numRows1000
s 由英文字母(小写和大写)、',''.' 组成

【分析】


在这里插入图片描述

如上图所示,我们用数字来观察规律,设行数为 n n n

首先看第一行,0到6一共间隔 2 n − 2 2n-2 2n2 个数,因为从0走到3有 n − 1 n-1 n1 个数,3走到6也有 n − 1 n-1 n1 个数,因此第一行为从0开始的公差为 2 n − 2 2n-2 2n2 的等差数列。同理最后一行为从 n − 1 n-1 n1 开始的公差为 2 n − 2 2n-2 2n2 的等差数列。

对于中间行,以第二行为例,由两个等差数列组成,一个是在直线上的数列:1、7、13,这是从1开始的公差为 2 n − 2 2n-2 2n2 的等差数列;还有一个是在斜线上的数列:5、11、17,这是从5(可以看成 2 n − 2 − i 2n-2-i 2n2i i i i 表示这一行的第一个数)开始的公差为 2 n − 2 2n-2 2n2 的等差数列。因此中间行就是先输出第一个等差数列的第一项,然后输出第二个等差数列的第一项,再输出第一个等差数列的第二项,以此类推。


【代码】

class Solution {
public:
    string convert(string s, int n) {
        if (n == 1) return s;  // 特判
        string res;
        for (int i = 0; i < n; i++)
        {
            if (i == 0 || i == n - 1)  // 第一行或最后一行
                for (int j = i; j < s.size(); j += 2 * n - 2)
                    res += s[j];
            else
                // j表示第一个等差数列,k表示第二个等差数列
                for (int j = i, k = 2 * n - 2 - i; j < s.size() || k < s.size(); j += 2 * n - 2, k += 2 * n - 2)
                {
                    if (j < s.size()) res += s[j];
                    if (k < s.size()) res += s[k];
                }
        }
        return res;
    }
};

LeetCode 7. 整数反转(中等)

【题目描述】

给你一个 32 位的有符号整数 x ,返回将 x 中的数字部分反转后的结果。
如果反转后整数超过 32 位的有符号整数的范围 [ − 2 31 , 2 31 − 1 ] [-2^{31}, 2^{31} - 1] [231,2311],就返回 0。
假设环境不允许存储 64 位整数(有符号或无符号)。

【示例1】

输入:x = 123
输出:321

【示例2】

输入:x = -123
输出:-321

【示例3】

输入:x = 120
输出:21

【示例4】

输入:x = 0
输出:0

【提示】

− 2 31 ≤ x ≤ 2 31 − 1 -2^{31}\le x\le 2^{31} - 1 231x2311

【分析】


首先有个小 Tips:C++中负数取模的结果也为负数,如 − 1234 % 10 = − 4 -1234\% 10=-4 1234%10=4

直接用 x % 10 x\% 10 x%10 求出 x x x 的个位数 a a a,然后 r e s = r e s ∗ 10 + a res=res*10+a res=res10+a,根据负数取模的特性易知该方式同样适用于负数。

注意我们当做无法使用 long long 类型,因此做溢出判断的时候需要对判断公式做一个变换。


【代码】

class Solution {
public:
    int reverse(int x) {
        int res = 0;
        while (x)
        {
            // res * 10 + x % 10 > MAX -> res > (MAX - x % 10) / 10
            if (res > 0 && res > (INT_MAX - x % 10) / 10) return 0;
            if (res < 0 && res < (INT_MIN - x % 10) / 10) return 0;
            res = res * 10 + x % 10;  // x不管正负都通用
            x /= 10;
        }
        return res;
    }
};

LeetCode 8. 字符串转换整数-atoi(中等)

【题目描述】

请你来实现一个 myAtoi(string s) 函数,使其能将字符串转换成一个 32 位有符号整数(类似 C/C++ 中的 atoi 函数)。
函数 myAtoi(string s) 的算法如下:

  1. 读入字符串并丢弃无用的前导空格
  2. 检查下一个字符(假设还未到字符串末尾)为正还是负号,读取该字符(如果有)。确定最终结果是负数还是正数。如果两者都不存在,则假定结果为正。
  3. 读入下一个字符,直到到达下一个非数字字符或到达输入的结尾。字符串的其余部分将被忽略。
  4. 将前面步骤读入的这些数字转换为整数(即,“123” -> 123,“0032” -> 32)。如果没有读入数字,则整数为 0。必要时更改符号(从步骤 2 开始)。
  5. 如果整数数超过 32 位有符号整数范围 [ − 2 31 , 2 31 − 1 ] [-2^{31}, 2^{31} - 1] [231,2311],需要截断这个整数,使其保持在这个范围内。具体来说,小于 − 2 31 -2^{31} 231 的整数应该被固定为 − 2 31 -2^{31} 231,大于 2 31 − 1 2^{31}-1 2311 的整数应该被固定为 − 2 31 − 1 -2^{31}-1 2311
  6. 返回整数作为最终结果。

注意:

  • 本题中的空白字符只包括空格字符 ' '
  • 除前导空格或数字后的其余字符串外,请勿忽略任何其他字符。

【示例1】

输入:s = "42"
输出:42

【示例2】

输入:s = "   -42"
输出:-42

【示例3】

输入:s = "4193 with words"
输出:4193

【提示】

0 ≤ s . l e n g t h ≤ 200 0\le s.length\le 200 0s.length200
s 由英文字母(大写和小写)、数字(0-9)、' ''+''-''.' 组成

【分析】


模拟处理字符串即可,判断溢出的方式与上一题相似,唯一的一个坑点是负数的最小值的绝对值是比正数的最大值多1的,因此当恰好等于最小值时 r e s res res 存不下对应的正数,因此需要直接返回最小值。


【代码】

class Solution {
public:
    int myAtoi(string s) {
        int idx = 0;
        while (idx < s.size() && s[idx] == ' ') idx++;  // 过滤前导空格
        int op = 1;  // 标记正负,没有正负号时默认为正
        if (s[idx] == '-') op *= -1, idx++;
        else if (s[idx] == '+') idx++;
        int res = 0;
        while (idx < s.size() && s[idx] >= '0' && s[idx] <= '9')
        {
            int x = s[idx] - '0';
            // res * 10 + x > MAX -> res > (MAX - x) / 10
            if (op > 0 && res > (INT_MAX - x) / 10) return INT_MAX;
            // -res * 10 - x < MIN -> -res < (MIN + x) / 10
            if (op < 0 && -res < (INT_MIN + x) / 10) return INT_MIN;
            if (-res * 10 - x == INT_MIN) return INT_MIN;  // 特判刚好等于最小值
            res = res * 10 + x;
            idx++;
        }
        return res * op;
    }
};

LeetCode 9. 回文数(简单)

【题目描述】

给你一个整数 x,如果 x 是一个回文整数,返回 true;否则,返回 false
回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。
例如,121 是回文,而 123 不是。

【示例1】

输入:x = 121
输出:true

【示例2】

输入:x = -121
输出:false
解释:从左向右读, 为 -121 。 从右向左读, 为 121- 。因此它不是一个回文数。

【示例3】

输入:x = 10
输出:false
解释:从右向左读, 为 01 。因此它不是一个回文数。

【提示】

− 2 31 ≤ x ≤ 2 31 − 1 -2^{31}\le x\le 2^{31} - 1 231x2311

【分析】


简单题,可以转换成字符串来做,也可以使用数值方法来做,用之前的方法逐步将 x x x 的个位取出来构建出新的数,然后判断两数是否相等即可。


【代码】

【字符串解法】

class Solution {
public:
    bool isPalindrome(int x) {
        if (x < 0) return false;  // 负数一定不是回文数
        string s = to_string(x);
        return s == string(s.rbegin(), s.rend());
    }
};

【数值解法】

class Solution {
public:
    bool isPalindrome(int x) {
        if (x < 0) return false;  // 负数一定不是回文数
        long long res = 0;  // 1234567899之类的数翻转后会溢出int
        int tmp = x;
        while (tmp) res = res * 10 + tmp % 10, tmp /= 10;
        return res == x;
    }
};

LeetCode 10. 正则表达式匹配(困难)

【题目描述】

给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 '.''*' 的正则表达式匹配。

  • '.' 匹配任意单个字符
  • '*' 匹配零个或多个前面的那一个元素
  • 所谓匹配,是要涵盖整个字符串 s 的,而不是部分字符串。

【示例1】

输入:s = "aa", p = "a"
输出:false
解释:"a" 无法匹配 "aa" 整个字符串。

【示例2】

输入:s = "aa", p = "a*"
输出:true
解释:因为 '*' 代表可以匹配零个或多个前面的那一个元素, 在这里前面的元素就是 'a'。因此,字符串 "aa" 可被视为 'a' 重复了一次。

【示例3】

输入:s = "ab", p = ".*"
输出:true
解释:".*" 表示可匹配零个或多个('*')任意字符('.')

【提示】

1 ≤ s . l e n g t h ≤ 20 1\le s.length\le 20 1s.length20
1 ≤ p . l e n g t h ≤ 20 1\le p.length\le 20 1p.length20
s 只包含从 a-z 的小写字母
p 只包含从 a-z 的小写字母,以及字符 .*
保证每次出现字符 * 时,前面都匹配到有效的字符

【分析】


这题很难想到是 DP 问题,因此难度不小。我们一步步分析状态表示和状态计算:

  • 状态表示 f [ i ] [ j ] f[i][j] f[i][j]
    • 集合:所有 s [ 1 ∼ i ] s[1\sim i] s[1i] p [ 1 ∼ j ] p[1\sim j] p[1j](下标从1开始)的匹配方案。
    • 属性:bool 类型,表示是否存在一个合法方案。
  • 状态计算:
    • p [ j ] ≠ ∗ p[j]\ne * p[j]=,那么直接看 s [ i ] s[i] s[i] p [ j ] p[j] p[j] 是否匹配即可,若 s[i] == p[j] 或者 p[j] == '.',且满足 s s s 的前 i − 1 i-1 i1 个字符和 j j j 的前 j − 1 j-1 j1 个字符也匹配,那么 s [ i ] s[i] s[i] p [ j ] p[j] p[j] 匹配,即可以写出以下状态转移方程:
      f[i][j] = f[i - 1][j - 1] && (s[i] == p[j] || p[j] == '.')
    • p [ j ] = ∗ p[j]=* p[j]=,那么我们需要枚举一下 * 表示多少个字符,如果表示0个字符,则 s s s 的前 i i i 个字符和 j j j 的前 j − 2 j-2 j2 个字符匹配;如果表示1个字符,则 s s s 的前 i − 1 i-1 i1 个字符和 j j j 的前 j − 2 j-2 j2 个字符匹配,且 s[i] == p[j - 1];如果表示2个字符,则 s s s 的前 i − 2 i-2 i2 个字符和 j j j 的前 j − 2 j-2 j2 个字符匹配,且 s[i - 1] == p[j - 1] && s[i] == p[j - 1]。因此可以写出以下状态转移方程(没有将 p[j - 1] == '.' 写进去,别忘了这种情况也算匹配):
      f[i][j] = f[i][j - 2] || (f[i - 1][j - 2] && s[i] == p[j - 1]) || (f[i - 2][j - 2] && s[i - 1] == p[j - 1] && s[i] == p[j - 1]) ...
      现在我们进行优化,写出 f[i - 1][j] 的状态转移方程如下:
      f[i - 1][j] = f[i - 1][j - 2] || (f[i - 2][j - 2] && s[i - 1] == p[j - 1]) ...
      因此可以写出优化后的状态转移方程(将 p[j - 1] == '.' 考虑进去):
      f[i][j] = f[i][j - 2] || f[i - 1][j] && (s[i] == p[j - 1] || p[j - 1] == '*')

【代码】

class Solution {
public:
    bool isMatch(string s, string p) {
        int n = s.size(), m = p.size();
        s = ' ' + s, p = ' ' + p;  // 在首部加一个空格,因为我们要从第一位开始
        vector<vector<bool>> f(n + 1, vector<bool>(m + 1));
        f[0][0] = true;
        for (int i = 0; i <= n; i++)
            for (int j = 1; j <= m; j++)  // p为空肯定无法匹配,而s为空不一定
            {
                if (i && p[j] != '*')  // 注意i不能为0,因为需要使用f[i - 1]
                    f[i][j] = f[i - 1][j - 1] && (s[i] == p[j] || p[j] == '.');
                else if (p[j] == '*')
                    // 同样注意i不能为0
                    f[i][j] = f[i][j - 2] || i && f[i - 1][j] && (s[i] == p[j - 1] || p[j - 1] == '.');
            }
        return f[n][m];
    }
};
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【LeetCode算法系列题解】第6~10题 的相关文章

随机推荐

  • 【HIT-计算机系统】ICS-Lab8 Dynamic Storage Allocator

    第1章 实验基本信息 1 1 实验目的 理解现代计算机系统虚拟存储的基本知识 掌握C语言指针相关的基本操作 深入理解动态存储申请 释放的基本原理和相关系统函数 用C语言实现动态存储分配器 并进行测试分析 培养Linux下的软件系统开发与测试
  • 30分钟部署一个kubernetes集群【1.15】

    作者 李振良 kubeadm是官方社区推出的一个用于快速部署kubernetes集群的工具 这个工具能通过两条指令完成一个kubernetes集群的部署 创建一个 Master 节点 kubeadm init 将一个 Node 节点加入到当
  • 解决window平台下的.ssh/id_rsa bad permission问题

    参考链接 https www cnblogs com clblacksmith p 11677135 html
  • 解决问题:List集合add元素,添加多个对象出现重复的问题

    首先我们在new 一个对象的时候 对象的id是唯一确定的 将对象add入list中时 放入list中的其实是对象的引用 而每次循环只是简单的set 对象的属性 set新的属性值 而add进list中的对象还是同一个对象id 也就是同一个对象
  • 渗透必备工具-BurpSuite

    目录 介绍 爆破 解码 BurpSuite burpsuite基本可以说是渗透的必备工具 用起来也很简单 方便 通常使用它可以进行一些截包分析 修改包数据 暴力破解 扫描等很多功能 用得最多的应该是开代理截包分析数据和爆破 解码 加密 bu
  • Ubuntu 22.04 LTS root登录、修改当前用户名和主机名

    前言 Ubuntu 22 04 默认不开启root用户 配置操作 1 开启 root 以普通用户登录系统 创建root用户的密码 opt opt sudo passwd root SSH 放行 opt opt sudo sed i s Pe
  • jeecgboot 上传文件

    jeecgboot框架中文件上传接口 jeecg boot sys common upload 支持本地上传 配置云上传等多种方式上传文件 local为本地存储 还需要配置jeecg path upload minio为使用MinIO线上存
  • tcp/ip协议详解

    1 TCP IP协议族是一个四层协议系统 自低而上分别是数据链路层 网络层 传输层 应用层 1 数据链路层 实现了网卡接口的网络驱动程序 以处理数据在物理媒介上的传输 ARP协议 将目标机器的IP地址转换为其物理地址 数据链路层使用物理地址
  • Oracle_SQL_序列与groupby同时用

    暂做记录 大小 19 6 KB 查看图片附件
  • Re48:读论文 kNN-LMs Generalization through Memorization: Nearest Neighbor Language Models

    诸神缄默不语 个人CSDN博文目录 论文名称 Generalization through Memorization Nearest Neighbor Language Models 模型简称 kNN LMs 本文是2020年ICLR论文
  • Linux系统的组成

  • 过滤器(Filter)与拦截器(Interceptor )区别

    过滤器 Filter Servlet中的过滤器Filter是实现了javax servlet Filter接口的服务器端程序 主要的用途是设置字符集 控制权限 控制转向 做一些业务逻辑判断等 其工作原理是 只要你在web xml文件配置好要
  • uni-calendar日历组件日期范围默认选中及优化存在日期范围后点击第一下、第二下选中为下一日期范围

    1 日期范围默认选中 该组件未提供默认选择日期范围 需对组件进行修改 步骤如下 1 在 uni calendar 文件下找到 uni calendar vue 文件 props 中增加 defaultRange type Array def
  • Vue2.0中el-table的循环写法

    文章目录 一般写法 偷懒 写法 在有开发任务的一周 过得是相当快 这一周的开发学到不少东西 首先回忆一下在代码中使用到的table循环 一般写法 现在学会了 偷懒 之前写的代码就跟搬运工一样 表格中的每一列都会去写一行代码
  • php://filter绕过死亡exit

    文章目录 php filter绕过死亡exit 前言 EIS 2019 EzPOP 绕过exit 参考 php filter绕过死亡exit 前言 最近写了一道反序列化的题 其中有一个需要通过php filter去绕过死亡exit 的小tr
  • 事务回滚

    转自 https blog csdn net ProGram BlackCat article details 88230287 spring的事务边界是在调用业务方法之前开始的 业务方法执行完毕之后来执行commit or rollbac
  • 安装tensorflow-gpu和tensorflow_federated

    前言 在安装tensorflow gpu前要先安装CUDA和cuDNN 具体安装步骤可以见上一篇文章 记录Win10正确安装CUDA和cuDNN的过程 记录一些坑 安装tensorflow gpu 我电脑上安装的CUDA版本为10 2 cu
  • 专业三复习

    mysql复习 C Users 86131 gt mysql uroot proot C Users 86131 gt mysql uroot proot mysql gt show databases Database informati
  • [MySQL]获取某个字段中某个字符的个数

    例 获取account name字段中 的个数 select length account name length REPLACE account name from user
  • 【LeetCode算法系列题解】第6~10题

    CONTENTS LeetCode 6 N 字形变换 中等 LeetCode 7 整数反转 中等 LeetCode 8 字符串转换整数 atoi 中等 LeetCode 9 回文数 简单 LeetCode 10 正则表达式匹配 困难 Lee