顺序刷题

6. Z字形变换

将一个给定字符串根据给定的行数,以从上往下、从左到右进行 Z 字形排列。

比如输入字符串为 "LEETCODEISHIRING" 行数为 3 时,排列如下:

L   C   I   R
E  T  O  E S  I  I  G
E   D   H   N

之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:"LCIRETOESIIGEDHN"

请你实现这个将字符串进行指定行数变换的函数:

string convert(string s, int numRows);

示例 1:

输入: s = “LEETCODEISHIRING”, numRows = 3
输出: “LCIRETOESIIGEDHN”

示例 2:

输入: s = “LEETCODEISHIRING”, numRows = 4
输出: “LDREOEIIECIHNTSG”
解释:

L   D   R
E  O E  I I
E C  I H  N
T   S    G

解题思路:

  • 题目理解:

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

numRows 行字符串分别为 s_1, s_2,…, s_n,则容易发现:按顺序遍历字符串 s 时,每个字符 cZ 字形中对应的 行索引 先从 s_1增大至 s_n,再从 s_n减小至 s_1 …… 如此反复。

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

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

zWord[row] += c: 把每个字符 c 填入对应行 s_{row}

row += goingDown: 更新当前字符 c 对应的行索引;

goingDown = - goingDown: 在达到 Z 字形转折点时,执行反向。

  • 复杂度分析:

    时间复杂度 O(N) :遍历一遍字符串 s
    空间复杂度 O(N) :各行字符串共占用 O(N) 额外空间。

class Solution {
public:
    string convert(string s, int numRows) {
        if (numRows == 1) return s;//注意特判,numRows为1时,不会向下或向上拼接,只输出一行也就是原字符串,后面的程序会失效越界。
        vector<string> zWord(min((int) s.length(), numRows));//防止出现字符串长度小于行数的情况,s.length()返回类型是size_t,所以要强制类型转换一下才能用min
        int goingDown = -1;//行数加一或减一,初始值应为-1,因为第一次循环进判断会取反,这是第一行。
        int row = 0;//从第一行开始
        for (auto &c:s) {//遍历字符串
            zWord[row] += c;//把字符拼接在应该在的那一行的字符串上
            if (row == 0 || row == numRows - 1) {//第一行或最后一行就方向调转,注意是0和numRows-1
                goingDown = -goingDown;//遇到第一行应该为1,最后一行应该为-1
            }
            row += goingDown;//行数逐渐增加或减小,来回振荡
        }
        string ans;
        for (auto &s:zWord) {//从上到下遍历行
            ans += s;//把z变换后的每一行拼接起来
        }
        return ans;
    }
};

7. 整数反转

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

示例 1:

输入: 123
输出: 321

示例 2:

输入: -123
输出: -321

示例 3:

输入: 120
输出: 21

注意:

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

解题思路:

作者:wang_ni_ma
链接:leetcode-cn.com/problems/reverse-i...
来源:力扣(LeetCode)

首先我们想一下,怎么去反转一个整数?
用栈?
或者把整数变成字符串,再去反转这个字符串?
这两种方式是可以,但并不好。实际上我们只要能拿到这个整数的 末尾数字 就可以了。
12345为例,先拿到5,再拿到4,之后是321,我们按这样的顺序就可以反向拼接处一个数字了,也就能达到 反转 的效果。
怎么拿末尾数字呢?好办,用取模运算就可以了

顺序刷题

  1. 12345 % 10 得到5,之后将12345 / 10
  2. 1234 % 10 得到4,再将1234 / 10
  3. 123 % 10 得到3,再将123 / 10
  4. 12 % 10 得到2,再将12 / 10
  5. 1 % 10 得到1,再将1 / 10

这么看起来,一个循环就搞定了,循环的判断条件是x>0
但这样不对,因为忽略了 负数
循环的判断条件应该是while(x!=0),无论正数还是负数,按照上面不断的/10这样的操作,最后都会变成0,所以判断终止条件就是!=0
有了取模和除法操作,对于像12300这样的数字,也可以完美的解决掉了。

看起来这道题就这么解决了,但请注意,题目上还有这么一句

  • 假设我们的环境只能存储得下 32 位的有符号整数,则其数值范围为 [−2^31, 2^31 − 1]

也就是说我们不能用long存储最终结果,而且有些数字可能是合法范围内的数字,但是反转过来就超过范围了。
假设有1147483649这个数字,它是小于最大的32位整数2147483647的,但是将这个数字反转过来后就变成了9463847411,这就比最大的32位整数还要大了,这样的数字是没法存到int里面的,所以肯定要返回0(溢出了)。
甚至,我们还需要提前判断

顺序刷题

上图中,绿色的是最大32位整数
第二排数字中,橘子的是5,它是大于上面同位置的4,这就意味着5后跟任何数字,都会比最大32为整数都大。
所以,我们到最大数的1/10时,就要开始判断了
如果某个数字大于 214748364那后面就不用再判断了,肯定溢出了。
如果某个数字等于 214748364呢,这对应到上图中第三、第四、第五排的数字,需要要跟最大数的末尾数字比较,如果这个数字比7还大,说明溢出了。

对于负数也是一样的

顺序刷题

上图中绿色部分是最小的32位整数,同样是在最小数的 1/10时开始判断
如果某个数字小于 -214748364说明溢出了
如果某个数字等于 -214748364,还需要跟最小数的末尾比较,即看它是否小于8

TIPS

通过包含头文件<limits.h>使用INT_MAXINT_MIN而不用直接打数字,使程序阅读更直观。

class Solution {
public:
    int reverse(int x) {
        int ans = 0;
        while (x) {//最后一位加上后x=0
            int temp = x % 10;
            //只需要考虑最后一次是否越界,因为后面相加如果越界了就无法判断了,所以要在少一位时判断前n-1位以及最后一位与INT_MAX和INT_MIN前n-1位以及最后一位比较
            if (ans > INT_MAX / 10 || (ans == INT_MAX / 10 && temp > INT_MAX % 10))
                return 0;//前n-1位大了,后面就不用看,如果前n-1位一样,看最后一位是否溢出
            if (ans < INT_MIN / 10 || (ans == INT_MIN / 10 && temp < INT_MIN % 10))
                return 0;//前n-1位小了,后面就不用看,如果前n-1位一样,看最后一位是否溢出
            ans = ans * 10 + temp;//溢出返回0,不溢出正常运算
            x /= 10;
        }
        return ans;
    }
};

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

请你来实现一个 atoi 函数,使其能将字符串转换成整数。

首先,该函数会根据需要丢弃无用的开头空格字符,直到寻找到第一个非空格的字符为止。接下来的转化规则如下:

  • 如果第一个非空字符为正或者负号时,则将该符号与之后面尽可能多的连续数字字符组合起来,形成一个有符号整数。
  • 假如第一个非空字符是数字,则直接将其与之后连续的数字字符组合起来,形成一个整数。
  • 该字符串在有效的整数部分之后也可能会存在多余的字符,那么这些字符可以被忽略,它们对函数不应该造成影响。

注意:假如该字符串中的第一个非空格字符不是一个有效整数字符、字符串为空或字符串仅包含空白字符时,则你的函数不需要进行转换,即无法进行有效转换。

在任何情况下,若函数不能进行有效的转换时,请返回 0 。

提示:

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

示例 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 (−231) 。

普通解法

    int myAtoi(string s) {
        if (s.empty())return 0;
        int ans=0;
        int flag=1;
        int i=0;
        while (s[i]==' ') ++i;//跳过空格
        if (s[i] == '-') flag=-1;//遇到的第一个非空字符是符号,标识位变为负一
        if (s[i] =='+' || s[i] == '-') ++i;//这一步比较妙,如果第一个非空字符是正负号,正常跳过
        while (i<s.size() && isdigit(s[i])) {//用isdigit()函数判断是不是整数
            int num=s[i]-'0';//如果是整数,要把字符变为整数,后面就不要用s[i]了,已经变成数字num了
            if (ans>INT32_MAX/10 || (ans==INT32_MAX/10 && num>7)){//这一步非常妙,因为最大最小除了最后一位一样,而最后一位最大是7,最小是8,
                // 所以判断只要大于7,8虽然越上界没越下界,但可以看作越界,因为只有输入是INT32_MIN,才会判断最后一位是否是8,与越界情况输出一样
                //因为如果以大于8作为界是非常困难的,因为字符本来是正数,8就会超过INT_MAX,这样做可谓一举两得
                return flag>0? INT32_MAX:INT32_MIN;//如果越界了,根据flag来判断返回正还是负
            }
            ans=ans*10+num;//把整数拼接到结果整数上
            ++i;
        }
        return flag>0? ans:-ans;//最后再根据flag输出正数或负数
    }

自动机

作者:LeetCode-Solution
链接:leetcode-cn.com/problems/string-to...
来源:力扣(LeetCode)

有点类似数电里的状态机,逻辑基本一样,转换成代码实现了。

思路

字符串处理的题目往往涉及复杂的流程以及条件情况,如果直接上手写程序,一不小心就会写出极其臃肿的代码。

因此,为了有条理地分析每个输入字符的处理方法,我们可以使用自动机这个概念:

我们的程序在每个时刻有一个状态 s,每次从序列中输入一个字符 c,并根据字符 c 转移到下一个状态 s'。这样,我们只需要建立一个覆盖所有情况的从 sc 映射到 s' 的表格即可解决题目中的问题。

算法

本题可以建立如下图所示的自动机:

顺序刷题

我们也可以用下面的表格来表示这个自动机:

‘ ‘ +/- number other
start start signed in_number end
signed end end in_number end
in_number end end in_number end
end end end end end

接下来编程部分就非常简单了:我们只需要把上面这个状态转换表抄进代码即可。

另外自动机也需要记录当前已经输入的数字,只要在 s'in_number 时,更新我们输入的数字,即可最终得到输入的数字。

class Automaton {
    string state = "start";
    unordered_map<string, vector<string>> table = {
        {"start", {"start", "signed", "in_number", "end"}},
        {"signed", {"end", "end", "in_number", "end"}},
        {"in_number", {"end", "end", "in_number", "end"}},
        {"end", {"end", "end", "end", "end"}}
    };

    int get_col(char c) {
        if (isspace(c)) return 0;
        if (c == '+' or c == '-') return 1;
        if (isdigit(c)) return 2;
        return 3;
    }
public:
    int sign = 1;
    long long ans = 0;

    void get(char c) {
        state = table[state][get_col(c)];
        if (state == "in_number") {
            ans = ans * 10 + c - '0';
            ans = sign == 1 ? min(ans, (long long)INT_MAX) : min(ans, -(long long)INT_MIN);
        }
        else if (state == "signed")
            sign = c == '+' ? 1 : -1;
    }
};

class Solution {
public:
    int myAtoi(string str) {
        Automaton automaton;
        for (char c : str)
            automaton.get(c);
        return automaton.sign * automaton.ans;
    }
};
本作品采用《CC 协议》,转载必须注明作者和本文链接
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!