顺序刷题
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
时,每个字符 c
在 Z 字形中对应的 行索引 先从 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
,之后是3
,2
,1
,我们按这样的顺序就可以反向拼接处一个数字了,也就能达到 反转 的效果。
怎么拿末尾数字呢?好办,用取模运算就可以了
- 将
12345 % 10
得到5
,之后将12345 / 10
- 将
1234 % 10
得到4
,再将1234 / 10
- 将
123 % 10
得到3
,再将123 / 10
- 将
12 % 10
得到2
,再将12 / 10
- 将
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_MAX
和INT_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'
。这样,我们只需要建立一个覆盖所有情况的从 s
与 c
映射到 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 协议》,转载必须注明作者和本文链接