LCR 018-验证回文串

Raphael Liu Lv10

给定一个字符串 s ,验证 s 是否是 **回文串 **,只考虑字母和数字字符,可以忽略字母的大小写。

本题中,将空字符串定义为有效的 **回文串 **。

示例 1:

**输入:** s = **** "A man, a plan, a canal: Panama"
**输出:** true
**解释:** "amanaplanacanalpanama" 是回文串

示例 2:

**输入:** s = "race a car"
**输出:** false
解释:"raceacar" 不是回文串

提示:

  • 1 <= s.length <= 2 * 105
  • 字符串 s 由 ASCII 字符组成

注意:本题与主站 125 题相同: https://leetcode-cn.com/problems/valid-palindrome/

前言

本题考查的是语言中常用字符(串)相关 API 的使用。本题解会给出解题思路以及参考代码,如果代码中有读者不熟悉的 API,可以自行查阅资料学习。

方法一:筛选 + 判断

最简单的方法是对字符串 s 进行一次遍历,并将其中的字母和数字字符进行保留,放在另一个字符串 sgood 中。这样我们只需要判断 sgood 是否是一个普通的回文串即可。

判断的方法有两种。第一种是使用语言中的字符串翻转 API 得到 sgood 的逆序字符串 sgood_rev,只要这两个字符串相同,那么 sgood 就是回文串。

[sol11-C++]
1
2
3
4
5
6
7
8
9
10
11
12
13
class Solution {
public:
bool isPalindrome(string s) {
string sgood;
for (char ch: s) {
if (isalnum(ch)) {
sgood += tolower(ch);
}
}
string sgood_rev(sgood.rbegin(), sgood.rend());
return sgood == sgood_rev;
}
};
[sol11-Python3]
1
2
3
4
class Solution:
def isPalindrome(self, s: str) -> bool:
sgood = "".join(ch.lower() for ch in s if ch.isalnum())
return sgood == sgood[::-1]
[sol11-Java]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Solution {
public boolean isPalindrome(String s) {
StringBuffer sgood = new StringBuffer();
int length = s.length();
for (int i = 0; i < length; i++) {
char ch = s.charAt(i);
if (Character.isLetterOrDigit(ch)) {
sgood.append(Character.toLowerCase(ch));
}
}
StringBuffer sgood_rev = new StringBuffer(sgood).reverse();
return sgood.toString().equals(sgood_rev.toString());
}
}

第二种是使用双指针。初始时,左右指针分别指向 sgood 的两侧,随后我们不断地将这两个指针相向移动,每次移动一步,并判断这两个指针指向的字符是否相同。当这两个指针相遇时,就说明 sgood 时回文串。

[sol12-C++]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution {
public:
bool isPalindrome(string s) {
string sgood;
for (char ch: s) {
if (isalnum(ch)) {
sgood += tolower(ch);
}
}
int n = sgood.size();
int left = 0, right = n - 1;
while (left < right) {
if (sgood[left] != sgood[right]) {
return false;
}
++left;
--right;
}
return true;
}
};
[sol12-Python3]
1
2
3
4
5
6
7
8
9
10
11
class Solution:
def isPalindrome(self, s: str) -> bool:
sgood = "".join(ch.lower() for ch in s if ch.isalnum())
n = len(sgood)
left, right = 0, n - 1

while left < right:
if sgood[left] != sgood[right]:
return False
left, right = left + 1, right - 1
return True
[sol12-Golang]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
func isPalindrome(s string) bool {
var sgood string
for i := 0; i < len(s); i++ {
if isalnum(s[i]) {
sgood += string(s[i])
}
}

n := len(sgood)
sgood = strings.ToLower(sgood)
for i := 0; i < n/2; i++ {
if sgood[i] != sgood[n-1-i] {
return false
}
}
return true
}

func isalnum(ch byte) bool {
return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9')
}
[sol12-Java]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Solution {
public boolean isPalindrome(String s) {
StringBuffer sgood = new StringBuffer();
int length = s.length();
for (int i = 0; i < length; i++) {
char ch = s.charAt(i);
if (Character.isLetterOrDigit(ch)) {
sgood.append(Character.toLowerCase(ch));
}
}
int n = sgood.length();
int left = 0, right = n - 1;
while (left < right) {
if (Character.toLowerCase(sgood.charAt(left)) != Character.toLowerCase(sgood.charAt(right))) {
return false;
}
++left;
--right;
}
return true;
}
}

复杂度分析

  • 时间复杂度:O(|s|),其中 |s| 是字符串 s 的长度。

  • 空间复杂度:O(|s|)。由于我们需要将所有的字母和数字字符存放在另一个字符串中,在最坏情况下,新的字符串 sgood 与原字符串 s 完全相同,因此需要使用 O(|s|) 的空间。

方法二:在原字符串上直接判断

我们可以对方法一中第二种判断回文串的方法进行优化,就可以得到只使用 O(1) 空间的算法。

我们直接在原字符串 s 上使用双指针。在移动任意一个指针时,需要不断地向另一指针的方向移动,直到遇到一个字母或数字字符,或者两指针重合为止。也就是说,我们每次将指针移到下一个字母字符或数字字符,再判断这两个指针指向的字符是否相同。

[sol2-C++]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Solution {
public:
bool isPalindrome(string s) {
int n = s.size();
int left = 0, right = n - 1;
while (left < right) {
while (left < right && !isalnum(s[left])) {
++left;
}
while (left < right && !isalnum(s[right])) {
--right;
}
if (left < right) {
if (tolower(s[left]) != tolower(s[right])) {
return false;
}
++left;
--right;
}
}
return true;
}
};
[sol2-Python3]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Solution:
def isPalindrome(self, s: str) -> bool:
n = len(s)
left, right = 0, n - 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 left < right:
if s[left].lower() != s[right].lower():
return False
left, right = left + 1, right - 1

return True
[sol2-Golang]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
func isPalindrome(s string) bool {
s = strings.ToLower(s)
left, right := 0, len(s) - 1
for left < right {
for left < right && !isalnum(s[left]) {
left++
}
for left < right && !isalnum(s[right]) {
right--
}
if left < right {
if s[left] != s[right] {
return false
}
left++
right--
}
}
return true
}

func isalnum(ch byte) bool {
return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9')
}
[sol2-Java]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Solution {
public boolean isPalindrome(String s) {
int n = s.length();
int left = 0, right = n - 1;
while (left < right) {
while (left < right && !Character.isLetterOrDigit(s.charAt(left))) {
++left;
}
while (left < right && !Character.isLetterOrDigit(s.charAt(right))) {
--right;
}
if (left < right) {
if (Character.toLowerCase(s.charAt(left)) != Character.toLowerCase(s.charAt(right))) {
return false;
}
++left;
--right;
}
}
return true;
}
}

复杂度分析

  • 时间复杂度:O(|s|),其中 |s| 是字符串 s 的长度。

  • 空间复杂度:O(1)。

 Comments
On this page
LCR 018-验证回文串