CodingTest/Leetcode

[LeetCode] 125.Valid Palindrome

Jay_J 2024. 7. 4. 00:45

문제:

 

문제는 간단하다. 입력값으로 문자열이 주어지고, 이 주어진 입력값 문자열을 모두 소문자로 바꾸고, 알파벳이 아닌 모든 특수문자들을 제거하고나서 이 문자열이 회문인지 판단하는 것이다. 사실 간단한 문제라 포스팅을 스킵 하려고 했지만 새로 쓰게된 라이브러리가 있어 기억하고자 포스팅을 하려고 한다!

 

아래는 내가 제출해 통과한 코드이다.

class Solution:
    def isPalindrome(self, s: str) -> bool:
        lower_s = s.lower()
        revised = re.sub(r'[^a-zA-Z0-9]', '', lower_s)

        reversed_s = revised[::-1]

        if revised == reversed_s:
            return True
        else:
            return False

 

- 먼저, 파이썬의 .lower()함수를 이용해 모든 문자열을 소문자로 바꿔준다. 다시한번 복기하지만, 파이썬의 문자열은 immutable한 불변의 객체이므로, lower()을 호출한다고 문자열 자체가 바뀌는것이 아니라 새로운 변수에 할당 해 주어야 한다.

 

- 이제, 문자를 제외한 모든 특수문자를 제거 해줘야 하는데, 내가 처음에 생각한 방식은 lower_s에 대해 for문을 돌며 .isalum()메서드를 호출하여 새로운 str = [] 배열에 알파벳이라면 append를 해주고, "".join(str)을 활용해 문자열을 합치려고 했지만 이것보다 더 괜찮은 메서드나 라이브러리가 있을까 싶어 찾아 보던 중 파이썬의 re라는 라이브러리를 발견하였다.

더보기

파이썬의 `re` 모듈은 정규 표현식을 사용하여 문자열을 검색, 매칭, 수정할 수 있는 강력한 도구를 제공합니다. 정규 표현식은 특정한 패턴을 가진 문자열을 찾거나, 대체하거나, 분리하는 데 사용됩니다. `re` 모듈을 사용하면 복잡한 문자열 처리 작업을 효율적으로 수행할 수 있습니다.

 

주요 함수 및 메서드

1. `re.match(pattern, string)`
   - 문자열의 시작에서 정규 표현식 패턴과 일치하는 부분을 찾습니다.
   - 일치하면 `MatchObject`를 반환하고, 일치하지 않으면 `None`을 반환합니다.

   import re
   match = re.match(r'\d+', '123abc')
   if match:
       print(match.group())  # 출력: 123


2. `re.search(pattern, string)`
   - 문자열 전체에서 정규 표현식 패턴과 일치하는 첫 번째 부분을 찾습니다.
   - 일치하면 `MatchObject`를 반환하고, 일치하지 않으면 `None`을 반환합니다.

   import re
   search = re.search(r'\d+', 'abc123def')
   if search:
       print(search.group())  # 출력: 123


4. `re.findall(pattern, string)`
   - 문자열에서 정규 표현식 패턴과 일치하는 모든 부분을 리스트로 반환합니다.

   import re
   matches = re.findall(r'\d+', 'abc123def456ghi')
   print(matches)  # 출력: ['123', '456']


5. **`re.finditer(pattern, string)`**
   - 문자열에서 정규 표현식 패턴과 일치하는 모든 부분을 반복 가능한 `MatchObject`로 반환합니다.

   import re
   matches = re.finditer(r'\d+', 'abc123def456ghi')
   for match in matches:
       print(match.group())  # 출력: 123 456


6. **`re.sub(pattern, repl, string)`**
   - 문자열에서 정규 표현식 패턴과 일치하는 부분을 다른 문자열로 대체합니다.

   import re
   result = re.sub(r'\d+', 'NUM', 'abc123def456ghi')
   print(result)  # 출력: abcNUMdefNUMghi


7. **`re.split(pattern, string)`**
   - 정규 표현식 패턴을 기준으로 문자열을 분리하여 리스트로 반환합니다.

   import re
   result = re.split(r'\d+', 'abc123def456ghi')
   print(result)  # 출력: ['abc', 'def', 'ghi']



 주요 정규 표현식 메타문자

- `.`: 모든 문자 (개행 문자를 제외하고)
- `^`: 문자열의 시작
- `$`: 문자열의 끝
- `*`: 0회 이상 반복
- `+`: 1회 이상 반복
- `?`: 0회 또는 1회 반복
- `{n}`: 정확히 n회 반복
- `{n,}`: n회 이상 반복
- `{n,m}`: n회 이상 m회 이하 반복
- `[]`: 문자 클래스 (예: `[a-z]`는 소문자 알파벳 a부터 z까지)
- `|`: OR 연산자 (예: `a|b`는 'a' 또는 'b')
- `()`: 그룹핑

 예제

import re

# 예제 문자열
text = "A man, a plan, a canal: Panama"

# 모든 알파벳 문자를 찾는 정규 표현식
pattern = re.compile(r'[a-zA-Z]')

# findall을 사용하여 모든 매칭 결과를 리스트로 반환
matches = pattern.findall(text)
print(matches)  # 출력: ['A', 'm', 'a', 'n', 'a', 'p', 'l', 'a', 'n', 'a', 'c', 'a', 'n', 'a', 'l', 'P', 'a', 'n', 'a', 'm', 'a']

# 모든 매칭 결과를 하나의 문자열로 결합
result = ''.join(matches)
print(result)  # 출력: AmanaplanacanalPanama


이 예제에서는 `re.compile`을 사용하여 정규 표현식을 컴파일하고, `findall` 메서드를 사용하여 모든 알파벳 문자를 찾아 하나의 리스트로 반환합니다. 그 후 `join` 메서드를 사용하여 모든 매칭 결과를 하나의 문자열로 결합합니다.

파이썬의 `re` 모듈을 사용하면 복잡한 문자열 처리 작업을 효율적으로 수행할 수 있습니다. 정규 표현식을 사용하여 다양한 패턴 매칭, 대체, 분리 작업을 쉽게 할 수 있습니다.

 

이처럼 re모듈은 복잡한 문자열 처리 작업을 효율적으로 수행할 수 있다. 그래서 

revised = re.sub(r'[^a-zA-Z0-9]', '', lower_s)

를 통해 모든 특수문자를 제거해 주었다. re.sub 함수는 정규 표현식 패턴 r'[^a-zA-Z0-9]'에 매칭되는 모든 문자를 빈 문자열 ''로 대체한다. [^a-zA-Z0-9]는 알파벳 소문자 a-z, 대문자 A-Z, 숫자 0-9가 아닌 모든 문자를 의미한다. 여기서 a앞에 표현된 ^는 부정의 의미로 알파벳 소문자 a-z, 대문자 A-Z, 숫자 0-9가 아닌 모든 문자를 의미한다.

 

그럼 이제, 여기까지의 과정을 마치면 주어진 입력값 문자열의 모든 문자가 소문자로 바뀌었고, 여기에 나타나는 모든 특수문자들을 제거했다.

 

-이제 회문인지 판단할 일만 남았다. 방금 re 모듈을 사용해 특수문자가 제거된 문자열을 뒤집었을때 같은 문자열이면 회문이라 판단하고 끝낸다. 여기서, 문자열을 쉽게 뒤집는 방법이 등장한다.

 

더보기

문자열을 뒤집는 `reversed_s = revised[::-1]` 구문에 대해 설명하겠다. 이 구문은 슬라이싱을 사용하여 문자열을 뒤집는 파이썬의 간단하고 효율적인 방법이다.

💬슬라이싱(slicing)

파이썬에서 슬라이싱은 시퀀스(예: 문자열, 리스트, 튜플)에서 특정 범위의 요소를 추출하는 데 사용된다. 슬라이싱 구문은 다음과 같은 형태를 가진다:

sequence[start:stop:step]


- `start`: 슬라이싱을 시작할 인덱스 (포함)
- `stop`: 슬라이싱을 끝낼 인덱스 (포함되지 않음)
- `step`: 요소를 추출할 간격. 양수이면 왼쪽에서 오른쪽, 음수이면 오른쪽에서 왼쪽으로 추출. 기본값은 1이다. 음수인 경우 역순으로 슬라이싱한다.

문자열을 뒤집기 위해 슬라이싱을 사용할 때 `start`와 `stop` 인덱스를 지정하지 않고 `step`을 -1로 설정하면, 문자열의 마지막 문자부터 첫 번째 문자까지 역순으로 추출한다. 즉, 문자열을 뒤집는 효과를 준다.

reversed_s = revised[::-1]


- `revised`: 원래 문자열
- `[::-1]`: 슬라이싱 구문으로, 문자열의 끝에서 시작하여 처음까지(-1) 한 문자씩(step) 역순으로 추출

아래는 이 구문을 사용한 예제이다:

# 원래 문자열
revised = "Hello, World!"

# 문자열을 뒤집음
reversed_s = revised[::-1]

# 결과 출력
print(reversed_s)  # 출력: "!dlroW ,olleH"


동작 설명
1. `revised[::-1]` 구문은 문자열 `revised`를 슬라이싱한다.
2. `start`와 `stop` 인덱스를 지정하지 않았으므로, 문자열 전체를 대상으로 한다.
3. `step`을 -1로 설정하여 문자열을 역순으로 추출한니다.
4. 결과적으로, `reversed_s`는 `revised`의 역순 문자열이 된다.