9. 回文数
- 判断一个整数是否是回文数。回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。
示例:
示例 1:
输入: 121
输出: true
示例 2:
输入: -121
输出: false
解释: 从左向右读, 为 -121 。 从右向左读, 为 121- 。因此它不是一个回文数。
示例 3:
输入: 10
输出: false
解释: 从右向左读, 为 01 。因此它不是一个回文数。
思路1:栈
将整数转为字符串,根据字符串长度为奇数还是偶数分两种情况讨论:
若为偶数,则先通过遍历将前一半字符入栈,再继续遍历,一边将栈中元素出栈,一边判断当前值是否与出栈元素相等,若不相等,直接返回False。
若为奇数,则先通过遍历将前一半字符入栈,再继续遍历时需要加1,跳过中间元素,然后一边将栈中元素出栈,一边判断当前值是否与出栈元素相等,若不相等,直接返回False。
代码实现1:
class Solution:
def isPalindrome(self, x: int) -> bool:
stack = []
s = str(x)
# 字符串长度为偶数
if len(s) % 2 == 0:
half = len(s) // 2
for i in range(half):
stack.append(s[i])
while stack:
if stack.pop() != s[half]:
return False
half += 1
# 字符串长度为奇数
elif len(s) % 2 != 0:
half = len(s) // 2
for i in range(half):
stack.append(s[i])
while stack:
if stack.pop() != s[half+1]: # 加1,跳过中间元素
return False
half += 1
return True
思路2:双端队列(复杂度: O ( n 2 ) O(n^2) O(n2))
将整数转为字符串,再转为列表,前后同时pop并比较,若不同,返回False。
代码实现2:
class Solution:
def isPalindrome(self, x: int) -> bool:
deque = list(str(x))
while len(deque) > 1:
if deque.pop(0) != deque.pop():
return False
return True
思路3:双指针(复杂度: O ( n ) O(n) O(n))
将整数转为字符串,两个指针一个指头一个指尾,向中间靠拢,若发现值不同,返回False。
代码实现3:
class Solution:
def isPalindrome(self, x):
s = str(x)
j = -1
for i in range(len(s) // 2):
if s[i] != s[j]:
return False
j -= 1
return True
思路4:利用python自由特性(复杂度: O ( n ) O(n) O(n))
将整数转为字符串,使用字符串翻转,判断翻转后的字符串是否与原字符串相等。
代码实现4:
class Solution:
def isPalindrome(self, x: int) -> bool:
return True if str(x)==str(x)[::-1] else False
思路5(提高):不转字符串(复杂度: O ( n ) O(n) O(n))
反转后面一半的整数,判断与前一半是否相等
定义reverse_x=0用于记录翻转的后半部分。
- 若x=1221:
当x>reverse_x时,执行以下操作:
x对10取余数,得到1,再将x除以10取整,
x=122,reverse_x = 0 * 10 + 1 = 1
x对10取余数,得到2,再将x除以10取整,
x=12,reverse_x = 1 * 10 + 2 = 12
此时x不再大于reverse_x,完成后半部分的翻转,判断二者是否相等。 - 若x=12321:
当x>reverse_x时,执行以下操作:
x对10取余数,得到1,再将x除以10取整,
x=1232,reverse_x = 0 * 10 + 1 = 1
x对10取余数,得到2,再将x除以10取整,
x=123,reverse_x = 1 * 10 + 2 = 12
x对10取余数,得到3,再将x除以10取整,
x=12,reverse_x = 12 * 10 + 3 = 123
此时x不再大于reverse_x,完成后半部分的翻转,将reverse_x//10后判断二者是否相等。
- 注意:" / " 表示浮点数除法,返回浮点结果;" // " 表示整数除法。
代码实现5:
class Solution:
def isPalindrome(self, x: int) -> bool:
if x < 0 or (x != 0 and x % 10 == 0):
return False
elif x == 0:
return True
else:
reverse_x = 0
while x > reverse_x:
remainder = x % 10
reverse_x = reverse_x * 10 + remainder
x = x // 10
# 当x的位数为奇数时, 只要满足 reverse_x//10 == x 即可
if reverse_x == x or reverse_x // 10 == x:
return True
else:
return False
思路6:整数翻转
代码实现:
class Solution:
def isPalindrome(self, x):
if x < 0:
return False
n = x
ans = 0
while n != 0:
j = n % 10
n //= 10
ans = ans * 10 + j
return ans == x
409. 最长回文串
- 给定一个包含大写字母和小写字母的字符串,找到通过这些字母构造成的最长的回文串。
在构造过程中,请注意区分大小写。比如 “Aa” 不能当做一个回文字符串。
示例:
输入: "abccccdd"
输出: 7
解释: 我们可以构造的最长的回文串是"dccaccd", 它的长度是 7。
思路:
- 获取字符串中每个字母出现的次数:
若都是偶数,则可以回文串的长度就是原始字符串长度;
若只有一个字母是奇数个,则可以将其置于中间,即回文串的长度依然为原始字符串长度;
若不止一个字母为奇数个,则每多一个,就要减一个字母,使其变为偶数。
代码实现:
class Solution:
def longestPalindrome(self, s: str) -> int:
s_set = set(s)
dlt = 0 # 要删掉的数目
flag = False # 如果字符串的数目出现奇数,就记录下来
for i in s_set:
# 只要字符串中有一个字符为奇数个,则要删除的数目就多一个
if s.count(i) % 2 != 0:
dlt += 1
flag = True
if flag:
# 字符串中出现几次奇数字符,就减几,再加上可以放在中间的一个字符
ret = len(s) - dlt + 1
else:
# 说明字符串中每个字符的个数全是偶数,直接返回原长度
ret = len(s)
return ret
647. 回文子串
- 给定一个字符串,你的任务是计算这个字符串中有多少个回文子串。
具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被计为是不同的子串。
示例:
示例 1:
输入: "abc"
输出: 3
解释: 三个回文子串: "a", "b", "c".
示例 2:
输入: "aaa"
输出: 6
说明: 6个回文子串: "a", "a", "a", "aa", "aa", "aaa".
思路:动态规划——从中心往两侧延伸
如图,对于长度为N的字符串,有2N-1个位置可能作为回文串的中心位置,即字母和字母之间。
对于每个可能的回文串中心位置,尽可能扩大它的回文区间 [left, right]。当 left >= 0 and right < N and S[left] == S[right] 时,扩大区间。此时回文区间表示的回文串为 S[left], S[left+1], …, S[right]。
在每一个中心位置向外延伸:
- 中心位置在字母处的,应记录当前字母为子串;
如对于2号位,应判断s[1] == s[1];对于8号位,应判断s[4] == s[4]。 - 中心文字在字母间的,应判断左右两个字母是否可构成回文串;
如对于3号位,应判断s[1] == s[2];对于9号位,应判断s[4] == s[5]。
即 left = center // 2,right = left + center % 2
代码实现:
class Solution:
def countSubstrings(self, s: str) -> int:
n = len(s)
ans = 0
for center in range(2 * n - 1):
left = center // 2
right = left + center % 2
while left >= 0 and right < n and s[left] == s[right]:
ans += 1
left -= 1
right += 1
return ans
5. 最长回文子串
- 给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。
示例:
示例 1:
输入: "babad"
输出: "bab"
注意: "aba" 也是一个有效答案。
示例 2:
输入: "cbbd"
输出: "bb"
思路:动态规划
- 先找出所有回文子串,在找的过程中使用length变量记录子串的长度,每找到一个更长的子串,就更新length,并记录下标,最后通过最长子串的下标返回最长子串。
代码实现:
class Solution:
def longestPalindrome(self, s: str) -> str:
# 如果字符串为空,直接返回
if s == '':
return s
n = len(s)
# 记录最长子串的长度,最短为一个字符
length = 1
# 使用字典来存储最长串的左右下标
dic = {}
# 寻找所有子串
for center in range(2 * n - 1):
left = center // 2
right = left + center % 2
while left >= 0 and right < n and s[left] == s[right]:
left -= 1
right += 1
# 在不满足循环条件退出时,left和right已经发生延长,如0位置,left = -1,right = 1 长度应为right - left - 1 = 1
if right - left - 1 > length:
# 记录左右下标位置,应为延长前的索引值
dic['left'] = left + 1
dic['right'] = right - 1
length = right - left - 1
# 若长度为1,返回第一个字符即可
if length == 1:
return s[0]
return s[dic['left']:dic['right']+1]