LeetCode 算法系列:第 6 ~ 10 题

目录

  1. Z 字形变换
  2. 整数反转
  3. 字符串转换整数 (atoi)
  4. 回文数
  5. 正则表达式匹配

1. Z 字形变换

题目描述

将一个给定字符串根据给定的行数,以从上往下、从左到右进行 Z 字形排列。 比如输入字符串为 “LEETCODEISHIRING” 行数为 3
时,排列如下:
L C I R
E T O E S I I G
E D H N

c++思路

(找规律) O(n)O(n)

这种按某种形状打印字符的题目,一般通过手画小图找规律来做。

我们先画行数是4的情况:

0 6 12

1 5 7 11 …

2 4 8 10

3 9

从中我们发现,对于行数是 n 的情况:

1.对于第一行和最后一行,是公差为 2(n−1) 的等差数列,首项是 0 和 n−1;

2.对于第 i 行(0<i<n−1),是两个公差为 2(n−1) 的等差数列交替排列,首项分别是 i 和 2n−i−2;

所以我们可以从上到下,依次打印每行的字符。

时间复杂度分析:每个字符遍历一遍,所以时间复杂度是O(n).

c++版本

class Solution {
public:
    string convert(string s, int numRows) {
        if(numRows==1) return s;
        string ans;
        for(int i =0;i<numRows;i++)
        {
            if(i==0||i==numRows-1)
            {
                for(int j=i;j<s.size();j+=2*(numRows-1))
                {
                    ans += s[j];
                }
            }
            else{
               for(int j=i,k=2*numRows-i-2;j<s.size()||k<s.size();j+=2*(numRows-1),k+=2*(numRows-1))
              {
                if(j<s.size()) ans += s[j];
                if(k<s.size()) ans += s[k];
              }
            }
            
        }
        return ans;
    }
};

python 思路

字符串 s 是以 Z 字形为顺序存储的字符串,目标是按行打印。

设 numRows 行字符串分别为s1,s2,…,sns1,s2,…,sn s_1, s_2 ,…,
s_ns1​,s2​,…,sn​ ,则容易发现:按顺序遍历字符串 s 时,

每个字符 c 在 Z 字形中对应的 行索引 先从 s1s1 s_1s1​ 增大至 snsn s_nsn​ ,再从
snsn s_nsn​ 减小至 s1s1 s_1s1​ …… 如此反复。

因此,解决方案为:模拟这个行索引的变化,在遍历 s 中把每个字符填到正确的行 res[i] 。

算法流程: 按顺序遍历字符串 s;

1.res[i] += c: 把每个字符 c 填入对应行 sisi s_isi​;

2.i += flag: 更新当前字符 c 对应的行索引;

3.flag = - flag: 在达到 ZZ 字形转折点时,执行反向。


class Solution(object):
    def convert(self, s, numRows):
        if(numRows==1):
            return s
        flag = -1
        i = 0
        ans =["" for _ in range(numRows)]
        for c in s:
            ans[i] += c
            if i ==0 or i == numRows-1:
                flag = -flag
            i +=flag
        return "".join(ans)
        """
        :type s: str
        :type numRows: int
        :rtype: str
        """

2. 整数反转

题目描述

给出一个 32 位的有符号整数,你需要将这个整数中每位上的数字进行反转。

示例1

输入: 123

输出: 321

示例2

输入: -123

输出: -321

示例3

输入: 120

输出: 21

注意:

假设我们的环境只能存储得下 32 位的有符号整数,则其数值范围为 [−2^31, 2^31−1] 请根据这个假设,如果反转后整数溢出那么就返回 0。

思路

依次从右往左计算出每位数字,然后逆序累加在一个整数中。

判断答案是否超出了int范围,超出的话返回0

c++版本

class Solution {
public:
    int reverse(int x) {
        int ans =0;
        while(x)
        {
           if(x>0&&ans>(INT_MAX-x%10)/10) return 0;
           if(x<0&&ans<(INT_MIN-x%10)/10) return 0;
           ans = ans*10+ x%10;
           x=x/10;
        }
        return ans;
    }
    
};

python版本

class Solution:
    def reverse(self, x: int) -> int:
        ans = 0
        c = 1 if x > 0 else -1
        x = abs(x)
        while(x):
            ans = ans * 10 + x % 10
            x //= 10
        return ans * c  if -2147483648 < ans * c < 2147483647 else 0

8. 字符串转换整数 (atoi)

题目描述

请你来实现一个 atoi 函数,使其能将字符串转换成整数。首先,该函数会根据需要丢弃无用的开头空格字符,直到寻找到第一个非空格的字符为止。接下来的转化规则如下:
1: 如果第一个非空字符为正或者负号时,则将该符号与之后面尽可能多的连续数字字符组合起来,形成一个有符号整数。
2:假如第一个非空字符是数字,则直接将其与之后连续的数字字符组合起来,形成一个整数。
3: 该字符串在有效的整数部分之后也可能会存在多余的字符,那么这些字符可以被忽略,它们对函数不应该造成影响。

注意:假如该字符串中的第一个非空格字符不是一个有效整数字符、字符串为空或字符串仅包含空白字符时,则你的函数不需要进行转换,即无法进行有效转换。在任何情况下,若函数不能进行有效的转换时,请返回 0 。
提示

本题中的空白字符只包括空格字符 ’ ’ 。 假设我们的环境只能存储 32 位大小的有符号整数,那么其数值范围为
[−2^31, 2^31−1]如果数值超过这个范围,请返回 INT_MAX , INT_MIN。

思路

示例 1:

输入: “42” 输出: 42

示例 2:

输入: " -42"
输出: -42
解释: 第一个非空白字符为 ‘-’, 它是一个负号。
我们尽可能将负号与后面所有连续出现的数字组合起来,最后得到 -42 。

示例 3:

输入: “4193 with words”
输出: 4193
解释: 转换截止于数字 ‘3’ ,因为它的下一个字符不为数字。

示例 4:

输入: “words and 987”
输出: 0
解释: 第一个非空字符是 ‘w’, 但它不是数字或正、负号。 因此无法执行有效的转换。

示例 5:

输入: “-91283472332”
输出: -2147483648
解释: 数字 “-91283472332” 超过 32位有符号整数范围。 因此返回 INT_MIN (−2^31) 。

思路

这道题的难点在于边界情况非常多,需要仔细考虑。

做这道题目时,推荐先写一个傻瓜版,然后提交,再根据Wrong Case去逐步添加针对各种情况的处理。

时间复杂度分析:假设字符串长度是 n,每个字符最多遍历一次,所以总时间复杂度是 O(n).

c++代码

class Solution {
public:
    int myAtoi(string str) {
        int k =0,ans =0,flag =1;
        while(str[k]==' '&& k<str.size()) k++;
        if(k==str.size()) return 0;
        if(str[k]=='-') {flag = -1;k++;}
        if(str[k]=='+')
        {
            if(flag == -1)
            {
                return 0;
            }
            k++;
        }
        while(k<str.size()&&str[k]>='0'&&str[k]<='9')
        {
            int x = str[k]-'0';
            if(flag>0&&ans>(INT_MAX-x)/10) return INT_MAX;
            if(flag<0&&-ans<(INT_MIN+x)/10) return INT_MIN;
            //when str = "-2147483648", the last step 2147483640+8 will overflow 
            if(-ans*10-x==INT_MIN) return INT_MIN;
            ans = ans*10+x;
            k++;
        }
        return ans*flag;
    }
};

python 思路

python可利用正则表达式来做:

^:匹配字符串开头

[+ -]:代表一个+字符或-字符

?:前面一个字符可有可无

\d:一个数字

+:前面一个字符的一个或多个

\D:一个非数字字符

*:前面一个字符的0个或多个

python 代码

class Solution:
    def myAtoi(self, str: str) -> int:
        return max(min(int(*re.findall('^[\+\-]?\d+', str.lstrip())), 2**31 - 1),-2**31)

9.回文数

题目描述
判断一个整数是否是回文数。回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。
示例 1:

输入: 121
输出: true

示例 2:

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

示例 3:

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

思路

直接将数字翻转容易超出范围,所以我们采用翻转一半的思想;

结尾为0的,除了0以外的数都不是回文数,先排除掉;

然后将数字的后半段翻转过来与前半段作比较,注意奇偶的问题即可。

c++版本

class Solution {
public:
    bool isPalindrome(int x) {
        int ans = 0;
        if(x < 0 || (x % 10 == 0 && x != 0)) return false;
        while(x > ans){
            ans = ans * 10 + x % 10;
            x /= 10;
        }
        if(ans == x || ans / 10 == x) return true;
        else return false;
    }
};

python 代码

class Solution:
    def isPalindrome(self, x: int) -> bool:
        ans = 0
        if x < 0 or (x % 10 == 0 and x != 0):
            return False
        while(x > ans):
            ans = ans * 10 + x % 10
            x //= 10
        if(x == ans or x == ans // 10):
            return True
        else:
            return False

10.正则表达式匹配

题目描述

给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 . 和’ * ’
的正则表达式匹配。 ‘.’ 匹配任意单个字符 ‘*’
匹配零个或多个前面的那一个元素

所谓匹配,是要涵盖 整个 字符串 s的,而不是部分字符串。
说明:

s 可能为空,且只包含从 a-z 的小写字母。
p 可能为空,且只包含从 a-z 的小写字母,以及字符 . 和 *。
示例 1:

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

示例 2:

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

示例 3:

输入: s = “aab” p = “c* a* b”
输出: true
解释: 因为 ‘*’ 表示零个或多个,这里 ‘c’ 为 0 个,
‘a’ 被重复一次。 因此可以匹配字符串 “aab”。

示例 4

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

示例 5:

输入: s = “mississippi” p = “mis* is* p*.”
输出: false

思路

(动态规划) O(nm)

1.设状态 f(i,j) 表示字符串 s 的前 i 个字符和字符串 p 的前 j 个字符能否匹配。

这里假设 s 和 p 的下标均从 1 开始。初始时,f(0,0)=true。

2.平凡转移 f(i,j)=f(i,j) | f(i−1,j−1),当 i > 0 且 s(i) == p(j) || p(j) == ‘.’。

3.当 p(j) == ’ * '时,若 j>=2,f(i,j) 可以从 f(i,j−2) 转移,表示丢弃这一次的 ‘*’ 和它之前的那个字符;

若 i > 0 且 s(i) == p(j - 1),表示这个字符可以利用这个 ‘ * ’ ,则可以从 f(i−1,j) 转移,表示利用 ‘*’。

4.初始状态 f(0,0) = true;循环枚举 i 从 0 到 n;j 从 1 到 m。因为 f(0,j) 有可能是有意义的,需要被转移更新。

5.最终答案为 f(n,m) c++代码

c++代码

class Solution {
public:
    bool isMatch(string s, string p) {
        int n = s.size();
        int 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++)
            {
                if(i>0&&(s[i]==p[j]||p[j]=='.'))
                {
                    f[i][j] = f[i][j]| f[i-1][j-1];
                }
                if(p[j]=='*')
                {
                    if(j>=2)
                    {
                        f[i][j] = f[i][j]| f[i][j-2];
                    }
                    if(i>0&&(s[i]==p[j-1]||p[j-1]=='.'))
                        f[i][j]=f[i][j]|f[i-1][j];
                }
            }
        }
        return f[n][m];
    }
};

python 代码

class Solution(object):
    def isMatch(self, s, p):
        n = len(s)
        m = len(p)
        s = ' '+s
        p = ' '+p
        f = [[False]*(m+1) for _ in range(n+1)]
        f[0][0]=True
        for i in range(n+1):
            for j in range(1,m+1):
                if(i>0 and (s[i]==p[j]or p[j]=='.')):
                    f[i][j] |= f[i-1][j-1]
                if p[j]=='*':
                    if j>=2:
                        f[i][j] |= f[i][j-2]
                    if i>0 and(s[i]==p[j-1]or p[j-1]=='.'):
                        f[i][j] |= f[i-1][j]
        return f[n][m]
        """
        :type s: str
        :type p: str
        :rtype: bool
        """
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值