5. 最长回文子串
给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。
示例:
示例 1:
输入: “babad”
输出: “bab”
注意: “aba” 也是一个有效答案。
示例 2:
输入: “cbbd”
输出: “bb”
思路:
对于一个长度为n字符串,字符串中可以作为回文中心的位置有2n-1个(每个字符和每两个字符的中间位置),对于每个回文中心,找到它的最长回文子串,最后保存最长的回文子串即可
代码:
class Solution(object):
def longestPalindrome(self, s):
"""
:type s: str
:rtype: str
"""
# 处理特殊情况
if s == "": return s
longest_palindrome = ""
for i in range(len(s)):
one_char_longest_palindrome = self.get_char_longest_palindrome(i, i, s)
if len(one_char_longest_palindrome) > len(longest_palindrome):
longest_palindrome = one_char_longest_palindrome
two_char_longest_palindrome = self.get_char_longest_palindrome(i, i+1, s)
if len(two_char_longest_palindrome) > len(longest_palindrome):
longest_palindrome = two_char_longest_palindrome
return longest_palindrome
def get_char_longest_palindrome(self, i, j, s):
"""
找到以i, j中点为中心的最长回文子串
以某个字符为中心,则i,j相同
以某两个相邻字符为中心,则j=i+1
:param i: 中心点左边的索引
:param j: 中心点右边的索引
:param s: 字符串
:return: 以i, j中点为中心的最长回文子串
"""
char_longest_palindrome = ""
imin = i
imax = j
while imin >=0 and imax < len(s):
if s[imin] == s[imax]:
char_longest_palindrome = s[imin:imax+1]
imin -= 1
imax += 1
else:
break
return char_longest_palindrome
分析:
时间复杂度:遍历所有中心点,遍历每个中心点的时间复杂度最差为O(n),所以最终的时间复杂度为O(n2)
空间复杂度:O(1),储存几个变量即可
6. 最长回文子串
将一个给定字符串根据给定的行数,以从上往下、从左到右进行 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
思路:
使用包含numRows个列表(每个列表可以看做是矩阵的某一行)的列表储存字符,2numRows-2是一个循环(numRows为1时,特殊处理),字符x根据其在字符串中的索引与2numRows-2的余数,决定字符x进入哪个列表,最后遍历列表的列表得到最终输出的字符串
代码:
class Solution(object):
def convert(self, s, numRows):
"""
:type s: str
:type numRows: int
:rtype: str
"""
# 边界情况,当numRows == 1时,loop_num为0会出错
if numRows == 1: return s
loop_num = 2 * numRows - 2 #loop_num是一个循环
# 初始化矩阵,每个列表作为一行
matrix = []
for i in range(min(numRows, len(s))):
matrix.append([])
# 将字符储存在矩阵中
for i in range(len(s)):
remainder = i % loop_num
if remainder <= loop_num / 2:
matrix[remainder].append(s[i])
else:
matrix[loop_num-remainder].append(s[i])
# 遍历列表得到结果
convert_string = ""
for row in matrix:
for char in row:
convert_string += char
return convert_string
分析:
时间复杂度:遍历一遍字符串和一个矩阵(使用列表存储可以避免存储矩阵中的空值,遍历储存空值的矩阵的时间复杂度为O(n2)),总的时间复杂度为O(n)
空间复杂度:O(n), 本程序只需要一个储存字符串中所有元素的矩阵即可
7. 整数反转
给出一个 32 位的有符号整数,你需要将这个整数中每位上的数字进行反转。
示例:
示例 1:
输入: 123
输出: 321
示例 2:
输入: -123
输出: -321
示例 3:
输入: 120
输出: 21
假设我们的环境只能存储得下 32 位的有符号整数,则其数值范围为 [−2^31, ^231 − 1]。请根据这个假设,如果反转后整数溢出那么就返回 0。
思路:
只需编写出正数的翻转即可,负数的只需要提取符号做正数的翻转然后加上符号即可,注意120->21的翻转过程(其实对于末尾的0不需要额外处理),注意溢出问题的处理,影响的时间和空间复杂度
代码:
class Solution(object):
def reverse(self, x):
"""
:type x: int
:rtype: int
"""
reverse_x = self.reverse_positive(x) if x >= 0 else - self.reverse_positive(x)
return reverse_x
def reverse_positive(self, x):
positive = 1 if x >= 0 else 0
x = x if x >= 0 else - x
reverse_x = 0
while x != 0:
reverse_x = 10 * reverse_x + x % 10
x = x // 10
# 判断是否越界
if (positive == 0 and -reverse_x <= - 2 ** 31) or (positive == 1 and reverse_x >= 2 ** 31 - 1):
return 0
return reverse_x
分析:
时间复杂度:O(log(n)), n是二进制的数位(n很大时,翻转后的数也可能不越界,如1000000000000…),log(n)是十进制的位数
空间复杂度:O(1),储存reverse_num,是有限位的,位数增多后溢出即停止
8. 字符串转换整数 (atoi)
请你来实现一个 atoi 函数,使其能将字符串转换成整数。
首先,该函数会根据需要丢弃无用的开头空格字符,直到寻找到第一个非空格的字符为止。
当我们寻找到的第一个非空字符为正或者负号时,则将该符号与之后面尽可能多的连续数字组合起来,作为该整数的正负号;假如第一个非空字符是数字,则直接将其与之后连续的数字字符组合起来,形成整数。
该字符串除了有效的整数部分之后也可能会存在多余的字符,这些字符可以被忽略,它们对于函数不应该造成影响。
注意:假如该字符串中的第一个非空格字符不是一个有效整数字符、字符串为空或字符串仅包含空白字符时,则你的函数不需要进行转换。
在任何情况下,若函数不能进行有效的转换时,请返回 0。
说明:
假设我们的环境只能存储 32 位大小的有符号整数,那么其数值范围为 [−2 ^31, 2 ^31 − 1]。如果数值超过这个范围,qing返回 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) 。
假设我们的环境只能存储得下 32 位的有符号整数,则其数值范围为 [−2^31, ^231 − 1]。请根据这个假设,如果反转后整数溢出那么就返回 0。
思路:
使用符号字典和数字字典储存符号和数字,整个函数的流程分为三个阶段:1.寻找第一个符合条件的首字符, 注意区分前一个字符为0还是不为0,2.连接数字字符,遇到第一个不符合要求的字符停止(不是数字的字符)3.检查值是否溢出
代码:
class Solution(object):
def myAtoi(self, str):
"""
:type str: str
:rtype: int
"""
if str == "":
return 0
num = 0
symnol = 0 # 符号位
pre_0 = 0 # 前一个字符是否为0
char_num = {"0":0, "1":1, "2":2, "3":3, "4":4, "5":5, "6":6, "7":7, "8":8, "9":9}
char_symbol = ["+", "-"]
index = 0
length = len(str)
# 先寻找第一个符合条件的字符
while index < length:
char = str[index]
if pre_0 == 0: # 前一个字符不为"0"的情况下
if char == " ":
index += 1
continue
if char == "0":
pre_0 = 1
index += 1
continue
if char not in char_num and char not in char_symbol:
return num
if char == "+":
symnol = 1
if char == "-":
symnol = -1
if char in char_num:
symnol = 1
num = char_num[char]
index += 1
break
else:
if char not in char_num:
return num
elif char == "0":
index += 1
continue
else:
symnol = 1
num = char_num[char]
index += 1
break
# 连接后后续的数字字符
while index < length:
char = str[index]
if char not in char_num:
break
num = num * 10 + char_num[char]
index += 1
# 输出最后的结果,需要判断范围
high = 2 ** 31 - 1
low = - 2 ** 31
if symnol * num > high:
result = high
elif symnol * num < low:
result = low
else:
result = symnol * num
return result
分析:
时间复杂度:O(n)扫描一遍字符串
空间复杂度:O(n)储存数值,提前加入溢出判断,可以是空间复杂度降为O(1)
9. 回文数
判断一个整数是否是回文数。回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。
示例:
示例 1:
输入: 121
输出: true
示例 2:
输入: -121
输出: false
解释: 从左向右读, 为 -121 。 从右向左读, 为 121- 。因此它不是一个回文数。
示例 3:
输入: 10
输出: false
解释: 从右向左读, 为 01 。因此它不是一个回文数。
进阶:
你能不将整数转为字符串来解决这个问题吗?
思路:
对于整数问题:1.负数不是回文数,2.计算正数的反向数值,反向数值与原数相等则是回文数,否者不是
对于字符串问题:两个指针分别从字符串左右扫描即可
代码:
class Solution(object):
def isPalindrome(self, x):
"""
:type x: int
:rtype: bool
"""
if x < 0:
return False
reverse_x = 0
tmp = x
while tmp != 0:
reverse_x = reverse_x * 10 + tmp % 10
tmp = tmp // 10
return True if reverse_x == x else False
分析:
时间复杂度:O(n)
空间复杂度:O(n)
10. 正则表达式匹配
给定一个字符串 (s) 和一个字符模式 §。实现支持 ‘.’ 和 ‘’ 的正则表达式匹配。
‘.’ 匹配任意单个字符。
'’ 匹配零个或多个前面的元素。
匹配应该覆盖整个字符串 (s) ,而不是部分字符串。
说明:
s 可能为空,且只包含从 a-z 的小写字母。
p 可能为空,且只包含从 a-z 的小写字母,以及字符 . 和 *。
示例:
示例 1:
输入:
s = “aa”
p = “a”
输出: false
解释: “a” 无法匹配 “aa” 整个字符串。
示例 2:
输入:
s = “aa”
p = “a*”
输出: true
解释: ‘*’ 代表可匹配零个或多个前面的元素, 即可以匹配 ‘a’ 。因此, 重复 ‘a’ 一次, 字符串可变为 “aa”。
示例 3:
输入:
s = “ab”
p = “."
输出: true
解释: ".” 表示可匹配零个或多个(’*’)任意字符(’.’)。
示例 4:
输入:
s = “aab”
p = “cab”
输出: true
解释: ‘c’ 可以不被重复, ‘a’ 可以被重复一次。因此可以匹配字符串 “aab”。
示例 5:
输入:
s = “mississippi”
p = “misisp*.”
输出: false
思路:
这个问题比较难,属于动态规划的区间模型,类似的问题还有构建字符的回文字符等,我们需要用一个矩阵表示s和p的匹配过程,想清楚矩阵中每个元素的构建过程是比较重要的,对于长度为n的s和长度为m的p,我们构建一个(n+1, m+1)的矩阵,矩阵M的行索引为[0, n],列索引为[0, m],矩阵中的每个元素(i, j),表示s的[0,i)和p的[0,j)子串是否匹配,矩阵的(0, 0)位置是1(即s和p的空白开头是匹配的),先填充矩阵第0行和第0列(这是边界情况,条件与矩阵内部的条件不太一样),然后填充矩阵的内部元素,最终的[n, m]即使能否匹配的结果。我们设置转移方程的时候,原则是匹配的优先级最高,也就是说想出所有可以匹配的情况,除此之外才是不匹配。
注意:
1.对于特殊字符“.”和“”的处理,“.”的处理比较简单,“”的处理复杂一些,需要进行分情况讨论
2.矩阵和字符串的索引问题,有一点绕,简单的讲矩阵i,j元素,对应的字符串元素为s[i-1]和p[j-1],如果字符串的索引是[1, len(str)]就会好理解很多。
具体逻辑见代码
代码:
import numpy as np
class Solution(object):
def isMatch(self, s, p):
"""
:type s: str
:type p: str
:rtype: bool
"""
if s == "" and p == "":
return True
dynamic_matrix = np.zeros(shape=(len(s)+1, len(p)+1))
dynamic_matrix[0][0] = 1
# 填写矩阵的第0行
for j in range(1, len(p)+1):
if p[j-1] == "*" and dynamic_matrix[0][j-2] == 1:
dynamic_matrix[0][j] = 1
else:
dynamic_matrix[0][j] = 0
# 填写矩阵的第0列
for i in range(1, len(s)+1):
dynamic_matrix[i][0] = 0
# 填写内部矩阵
for i in range(1, len(s)+1):
for j in range(1, len(p)+1):
if p[j-1] == "*":
# dynamic_matrix[i][j-2]是*将前一个字符重复0次,dynamic_matrix[i-1][j] and (s[i-1] == p[j-2] or p[j-2] == ".")是*将前一个字符重复1至多次,
# 问题是.*可以匹配任何字符,属于题中的一个漏洞,解决办法是当dynamic_matrix[i][j-2]为0,dynamic_matrix[i-1][j] and (s[i-1] == p[j-2] or p[j-2] == ".")为1时,让p[j-2]的值为s[i-1]
dynamic_matrix[i][j] = dynamic_matrix[i][j-2] or (dynamic_matrix[i-1][j] and (s[i-1] == p[j-2] or p[j-2] == "."))
else:
dynamic_matrix[i][j] = dynamic_matrix[i-1][j-1] and (s[i-1] == p[j-1] or p[j-1] == ".")
return True if dynamic_matrix[len(s)][len(p)] else False
分析:
时间复杂度:O(ps), 计算一个ps规模的矩阵
空间复杂度:O(ps), 构建一个ps规模的矩阵