目录
- Z 字形变换
- 整数反转
- 字符串转换整数 (atoi)
- 回文数
- 正则表达式匹配
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
"""