LeetCode 125 - Valid Palindrome (Easy)
LeetCode 125 - Valid Palindrome (Easy)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
// LeetCode 125 - Valid Palindrome (Easy)
// https://leetcode.com/problems/valid-palindrome/
// 문제 설명
// 대문자를 소문자로 바꾸고 영숫자가 아닌 문자를 모두 제거했을 때,
// 앞에서 읽으나 뒤에서 읽으나 같으면 팰린드롬이다.
// 문자열 s가 팰린드롬이면 true, 아니면 false를 반환하라.
// 제약 조건
// 1 <= s.length <= 2 * 10^5
// s는 출력 가능한 ASCII 문자로만 구성된다.
// Example 1
// Input : s = "A man, a plan, a canal: Panama"
// Output: true ("amanaplanacanalpanama")
//
// Example 2
// Input : s = "race a car"
// Output: false ("raceacar")
//
// Example 3
// Input : s = " "
// Output: true (영숫자 제거 후 빈 문자열 → 팰린드롬)
// 접근 — 양끝 투 포인터
// 새 문자열을 따로 만들지 않고, 원본을 양 끝에서 좁혀 들어가며 비교한다.
// 1) l(왼쪽)·r(오른쪽) 포인터를 양 끝에 둔다.
// 2) 영숫자가 아닌 문자는 l은 오른쪽으로, r은 왼쪽으로 건너뛴다.
// 3) 둘 다 영숫자에 멈추면 소문자로 바꿔 비교 — 다르면 즉시 false.
// 4) l < r 동안 반복, 끝까지 통과하면 true.
// 시간 O(n), 공간 O(1)
#include <string>
#include <cctype>
using namespace std;
class Solution {
public:
bool isPalindrome(string s) {
int l = 0, r = (int)s.size() - 1;
while (l < r) {
// 영숫자가 아니면 건너뛴다 (unsigned char 캐스팅: 음수 인덱스 UB 방지)
while (l < r && !isalnum((unsigned char)s[l])) ++l;
while (l < r && !isalnum((unsigned char)s[r])) --r;
if (tolower((unsigned char)s[l]) != tolower((unsigned char)s[r]))
return false;
++l;
--r;
}
return true;
}
};
정리
- 핵심은 영숫자만 추린 새 문자열을 만들지 않고 원본을 양 끝에서 비교한다는 점이다. 별도 버퍼를 쓰면 O(n) 공간이 들지만, 투 포인터로 건너뛰며 비교하면 공간이 O(1)로 떨어진다.
- 영숫자가 아닌 문자(
,, `,:등)는 비교 대상이 아니므로isalnum`이 false인 동안 포인터를 그냥 전진/후진시켜 건너뛴다. 두 포인터가 모두 영숫자에 멈췄을 때만 한 글자씩 맞춰 본다. isalnum·tolower에 넘기는 인자는unsigned char로 캐스팅했다.char가 부호 있는 타입인 환경에서 음수 값이 넘어가면 동작이 정의되지 않기 때문이다. ASCII 입력이라도 습관적으로 캐스팅해 두는 게 안전하다.- 안쪽
while에l < r조건을 같이 둔 게 중요하다." "처럼 영숫자가 하나도 없는 입력에서 포인터가 서로를 지나쳐 인덱스를 벗어나는 걸 막는다. 빈 문자열·공백뿐인 입력도 자연히 true로 처리된다. - 검증: 예제 3개 모두 통과(MSVC
/std:c++17컴파일·실행).
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.