谷歌历年面试真题——数组和字符串系列真题练习。
验证回文串
如果在将所有大写字符转换为小写字符、并移除所有非字母数字字符之后,短语正着读和反着读都一样。则可以认为该短语是一个 回文串 。
字母和数字都属于字母数字字符。
给你一个字符串 s,如果它是 回文串 ,返回 true ;否则,返回 false 。
示例 1:
输入: s = “A man, a plan, a canal: Panama”
输出: true
解释:“amanaplanacanalpanama” 是回文串。
示例 2:
输入: s = “race a car”
输出: false
解释: “raceacar” 不是回文串。
示例 3:
输入: s = " "
输出: true
解释: 在移除非字母数字字符之后,s 是一个空字符串 “” 。 由于空字符串正着反着读都一样,所以是回文串。
提示:
- 1 <= s.length <= 2 * 105
- s 仅由可打印的 ASCII 字符组成
思路一:双指针
一个简单的解决方案是首先对字符串进行预处理,将所有大写字母转换为小写字母,并去除非字母数字字符。然后利用双指针法,从字符串的两端开始向中间扫描,同时比较对应位置的字符是否相同。
具体步骤如下:
- 定义两个指针
left
和right
,分别指向字符串的起始和末尾。 - 循环直到
left
指针不小于right
指针:- 如果
s[left]
不是字母数字字符,则将left
指针右移一位。 - 如果
s[right]
不是字母数字字符,则将right
指针左移一位。 - 如果
s[left]
和s[right]
是字母数字字符,且它们不相等,则返回False
。 - 否则,将
left
指针右移一位,将right
指针左移一位。
- 如果
- 如果整个循环过程中都没有返回
False
,则说明字符串是回文串,返回True
。
下面是相应的 Python 代码实现:
def isPalindrome(s: str) -> bool:
# 预处理字符串,将所有大写字母转换为小写字母,并去除非字母数字字符
s = ''.join(ch.lower() for ch in s if ch.isalnum())
left, right = 0, len(s) - 1
while left < right:
# 跳过非字母数字字符
while left < right and not s[left].isalnum():
left += 1
while left < right and not s[right].isalnum():
right -= 1
# 比较对应位置的字符是否相同
if s[left] != s[right]:
return False
left += 1
right -= 1
return True
# 示例测试
print(isPalindrome("A man, a plan, a canal: Panama")) # 输出: True
print(isPalindrome("race a car")) # 输出: False
print(isPalindrome(" ")) # 输出: True
这种方法的时间复杂度为 O(n),其中 n 是字符串的长度。
思路二:栈
另一种解题思路是利用栈来解决。具体步骤如下:
- 首先对字符串进行预处理,将所有大写字母转换为小写字母,并去除非字母数字字符。
- 遍历预处理后的字符串,将每个字母数字字符压入栈中。
- 然后依次出栈并比较对应位置的字符是否相同,如果出现不相同的情况,则返回 False。
- 如果整个字符串遍历完成后都没有返回 False,则说明字符串是回文串,返回 True。
下面是相应的 Python 代码实现:
def isPalindrome(s: str) -> bool:
# 预处理字符串,将所有大写字母转换为小写字母,并去除非字母数字字符
s = ''.join(ch.lower() for ch in s if ch.isalnum())
stack = []
for ch in s:
stack.append(ch)
while len(stack) > 1:
if stack.pop() != stack.pop(0):
return False
return True
# 示例测试
print(isPalindrome("A man, a plan, a canal: Panama")) # 输出: True
print(isPalindrome("race a car")) # 输出: False
print(isPalindrome(" ")) # 输出: True
这种方法的时间复杂度为 O(n),其中 n 是字符串的长度。
思路三:双端比较
除了使用双指针和栈之外,还可以使用双指针的一种变种方法,称为双端比较。该方法在字符串预处理的同时,不断移动左右指针,直到它们相遇为止。具体步骤如下:
- 首先对字符串进行预处理,将所有大写字母转换为小写字母,并去除非字母数字字符。
- 定义左右指针
left
和right
,分别指向字符串的起始和末尾。 - 在移动左右指针的过程中,如果对应位置的字符不相同,则返回 False。
- 如果整个字符串遍历完成后都没有返回 False,则说明字符串是回文串,返回 True。
下面是相应的 Python 代码实现:
def isPalindrome(s: str) -> bool:
# 预处理字符串,将所有大写字母转换为小写字母,并去除非字母数字字符
s = ''.join(ch.lower() for ch in s if ch.isalnum())
left, right = 0, len(s) - 1
while left < right:
if s[left] != s[right]:
return False
left += 1
right -= 1
return True
# 示例测试
print(isPalindrome("A man, a plan, a canal: Panama")) # 输出: True
print(isPalindrome("race a car")) # 输出: False
print(isPalindrome(" ")) # 输出: True
这种方法的时间复杂度也是 O(n),其中 n 是字符串的长度。