
문자열은 문자들로 구성된 집합을 의미한다.
언어마다 표현하는 방법이 다르지만, 파이썬에서는 큰따옴표(”)와 작은따옴표(’) 문법을 모두 지원한다.
여러 줄으르 표기하기 위해 “””, ‘’’처럼 연속해서 3개의 따옴표를 사용하기도 한다.
문자열을 배울 때는 ‘따옴표로 감싸면 된다’ 처럼 간단하게 배우기 때문에 더 깊게 다뤄보진 않았을 것이다.
하지만 문자열의 작동 원리 같은 부분에서 문제가 나오기 때문에 문자열 자체에 대한 개념을 잘 아는 것이 중요하다.
문자열이란?
모든 것을 숫자로 이해하는 컴퓨터가 문자를 인식하도록 하려면 ‘문자를 숫자로 변환하는 과정’을 거쳐야 한다.
이 과정에서 유니코드라는 것을 사용한다.
변수에 문자열로 “Hello World!”를 할당하면 컴퓨터는 문자열을 각 글자에 해당하는 숫자로 인식한다.
그리고 모든 글자를 한 곳에 담아야 하기 때문에 암묵적으로 배열로 선언한다.
greeting = "Hello World!"
print(greeting[0]) # H
print(greeting[6:]) # World!
파이썬의 경우 한 번 선언된 문자열은 상수로 취급하기 때문에 값을 임의로 변경할 수 없다. 즉, 파이썬에서 문자열은 불변$_{immutable}$ 객체이다.
문자열을 +
연산자로 합치면 상수와 상수를 합치는 형태가 되어 새로운 상수를 생성하고 할당하는 과정에서 비효율적으로 연산량이 늘어날 수 있다. 이 점들을 유의하며 문제를 풀면 된다.
문자열 문제 유형
문자열 뒤집기
문자열을 통째로 뒤집는 것은 매우 쉽다.
문자열에 슬라이싱으로 [::-1]을 사용해 역으로 만들면 된다.
s = "Hello, World!"
reversed_s = s[::-1]
print(reversed_s) # !dlroW ,olleH
가장 흔한 문제로 팰린드롬$_{palindrome}$ 문제가 있다.
팰린드롬은 뒤집은 문자열이 원래 문자열하고 일치하는지 확인하는 간단한 문제이다.
def is_palindrome(s):
return s == s[::-1]
print(is_palindrome("racecar")) # True
print(is_palindrome("hello")) # False
여기서 더 난이도가 올라가게 되면 일부분만 문자를 뒤집을 때도 나오게 된다.
예를 들어 문자열의 특정 부분만 뒤집거나, 조건에 맞는 부분만 뒤집는 등의 변형된 문제가 될 수 있다.
다양한 방법을 통해 문자열을 뒤집을 수 있다. for
문을 사용해 수동으로 문자열을 뒤집거나, 투포인터나 슬라이싱 같은 방법을 사용할 수 있다.
for문 사용
def reverse_substring(s, start, end):
reversed_part = "" # 뒤집어진 부분 문자열
for i in range(start, end + 1):
reversed_part = s[i] + reversed_part
return s[:start] + reversed_part + s[end + 1:] # start + reversed_part + end를 모두 합쳐 최종 문자열 반환
s = "abcdefg"
reversed_s = reverse_substring(s, 2, 5)
print(reversed_s) # abfedcg
투포인터 사용
def reverse_substring(s, start, end):
s_list = list(s)
left, right = start, end
while left < right: # left 포인터가 right 포인터보다 작을 때까지 반복
s_list[left], s_list[right] = s_list[right], s_list[left] # 각 반복에서 s_list[left]와 s_list[right]의 값을 교환
left += 1
right -= 1
return ''.join(s_list) # 리스트 s_list를 다시 문자열로 결합해 반환
s = "abcdefg"
reversed_s = reverse_substring(s, 2, 5)
print(reversed_s) # abfedcg
아스키 코드 다루기
문자를 숫자로 바꿔주는 ord() 함수와 숫자를 문자로 바꿔주는 chr() 함수를 제공한다.
기본적으로 알파벳 순회 같은 문제에서 자주 등장한다.
ord() 함수
ord()
함수는 문자를 아스키 코드 값으로 변환한다.
print(ord('A')) # 65
print(ord('a')) # 97
chr() 함수
chr()
함수는 아스키 코드 값을 문자로 변환한다.
print(chr(65)) # 출력: A
print(chr(97)) # 출력: a
알파벳 순회
주어진 문자열의 각 문자를 아스키 코드 값으로 변환한 후, shift
값에 맞게 이동 후 다시 문자로 변환하는 과정을 보여준다.
def shift_alphabet(s, shift):
result = ""
for char in s:
if char.isalpha():
start = ord('A') if char.isupper() else ord('a')
result += chr(start + (ord(char) - start + shift) % 26)
else:
result += char
return result
s = "Hello, World!"
shifted_s = shift_alphabet(s, 3)
print(shifted_s) # Khoor, Zruog!
알파벳 대소문자 변환
대문자를 소문자로, 소문자를 대문자로 변환하는 문제도 해결할 수 있다.
def toggle_case(s):
result = ""
for char in s:
if char.isupper():
result += chr(ord(char) + 32)
elif char.islower():
result += chr(ord(char) - 32)
else:
result += char
return result
s = "Hello, World!"
toggled_s = toggle_case(s)
print(toggled_s) # hELLO, wORLD!
진법 변경하기
10진수를 다른 진법으로 변경하거나 다른 진법의 숫자를 10진수로 변경하는 문제가 등장할 수 있다.
다른 진법에서 10진수로 변환할 때는 int([변경하려는 숫자], [바꾸고 싶은 진법]) 표기법을 사용해 바로 변환할 수 있다.
binary_number = "1010"
decimal_number = int(binary_number, 2)
print(decimal_number) # 10
10진수에서 다른 진법으로 바꿀 때는 몇 가지의 과정을 더 거쳐야 한다.
10진수를 다른 진법으로 바꿀 때는 몇 가지 과정을 더 거쳐야 한다. 파이썬에서는 기본적으로 자주 사용하는 2진법 bin()
, 8진법 oct()
, 16진법 hex()
를 지원한다.
decimal_number = 10
# 10진수 -> 2진수
binary_number = bin(decimal_number)
print(binary_number) # 0b1010
# 10진수 -> 8진수
octal_number = oct(decimal_number)
print(octal_number) # 0o12
# 10진수 -> 16진수
hexadecimal_number = hex(decimal_number)
print(hexadecimal_number) # 0xa
파이썬 표준 라이브러리에서는 2, 8, 16진법 외의 진법 변환을 직접 제공하지 않으므로 따로 함수를 구현해야 한다.
# n을 base 진법으로 변경
def decimal_to_base(n, base):
if n == 0:
return "0"
digits = []
while n:
digits.append(int(n % base)) # n을 base로 나눈 나머지를 차례로 구하고
n //= base
return ''.join(str(x) for x in digits[::-1]) # 역순으로 결합해 반환
# 10진수 255 -> 7진수로 변환
decimal_number = 255
base = 7
converted_number = decimal_to_base(decimal_number, base)
print(converted_number) # 513
애너그램 확인하기
에너그램은 영단어의 철자 위치를 바꿔 다른 영단어를 만들어 내는 것을 의미한다.
예를 들어, “listen”과 “silent”는 서로 애너그램이다.
두 단어를 주고 이 단어가 애너그램인지 확인하는 문제가 많이 출제된다.
def is_anagram(s1, s2):
return sorted(s1) == sorted(s2)
print(is_anagram("listen", "silent")) # True
print(is_anagram("hello", "world")) # False
더 어렵게는 많은 단어를 주고 이 중에서 애너그램을 찾아내라는 문제가 나올 수도 있다.
from collections import defaultdict
def group_anagrams(words):
anagrams = defaultdict(list)
for word in words:
sorted_word = ''.join(sorted(word))
anagrams[sorted_word].append(word)
return list(anagrams.values())
words = ["eat", "tea", "tan", "ate", "nat", "bat"]
print(group_anagrams(words)) # [['eat', 'tea', 'ate'], ['tan', 'nat'], ['bat']]
철자의 위치만 바뀔 뿐 철자 자체가 변하지 않으므로 문자열을 정렬해 서로 비교해도 된다.
그러나 이 방법은 정렬을 사용하기 때문에 $O(n \space log \space n)$의 시간이 소요된다.
대신 딕셔너리를 활용해 각 단어의 철자 개수를 비교하면 시간을 $O(n)$으로 낮출 수 있다.
from collections import defaultdict
def is_anagram(s1, s2):
if len(s1) != len(s2):
return False
count = defaultdict(int)
for char in s1:
count[char] += 1
for char in s2:
count[char] -= 1
if count[char] < 0:
return False
return True
print(is_anagram("listen", "silent")) # True
print(is_anagram("hello", "world")) # False
문자열에서 원하는 문자 찾기
문자열에서 특정 문자를 찾는 문제는 검색 문제를 해결할 때 가장 많이 사용하는 형태이다. 특히 라이브러리 도움 없이 해결해야 하는 유형이기도 하다.
문제마다 요구하는 조건이 조금씩 다르지만, 기본적으로 탐색할 때 필요 없는 부분을 빠르게 제거하는 것이 핵심이다.
전체를 검색하는 상황이 아니라면 검색량을 줄일 수 있도록 조건이 제시되거나 검색하는 문자열의 크기가 매우 작게 나오는 편이다.
무작정 .find()
를 사용해 검색하기보다는 다른 방법이 없는지 한 번 더 생각해야 한다.
기본적인 검색 방법
find()
와 index()
를 사용해 문자열에서 특정 문자의 위치를 찾을 수 있다.
s = "Find character's index"
print(s.find('h')) # 6
print(s.index('h')) # 6
find()
는 문자가 발견되지 않으면 -1을 반환하고, index()
는 문자가 발견되지 않으면 예외를 발생시킨다.
특정 패턴 검색
정규 표현식을 사용하면 복잡한 패턴을 검색할 수 있다.
파이썬의 re
모듈을 사용해 정규 표현식 검색을 할 수 있다.
import re
s = "The rain in Spain"
match = re.search(r"\bS\w+", s) # 문자열 s에서 패턴 검색, 대문자 S와 그 뒤에 오는 하나 이상의 단어 문자를 포함하는 패턴
print(match.group()) # Spain
정규 표현식에 대한 설명
\b
: 단어의 경계를 나타냄. 단어의 시작이나 끝을 의미S
: 대문자 S를 찾음\w+
: 하나 이상의 단어 문자(알파벳, 숫자, 밑줄이 포함됨)를 찾음
기준에 맞춰 재정렬하기
알파벳 순서, 숫자 순서 등 특정 기준에 따라 문자열을 정렬하거나 추출하는 작업이 필요한 문제 유형이 있다.
이러한 문제는 기본적으로 난이도는 높지 않지만, 추가 조건이 주어지면서 복잡해질 수 있다.
예를 들어 정렬한 후 숫자나 문자의 합을 구하라거나, 가장 많이 사용된 단어를 찾는 등의 조건이 추가될 수 있다.
이런 문제를 해결하는데 있어 파이썬의 collections
모듈은 매우 유용하다.
이 모듈은 단어 빈도 수를 세는 것 이외에도 다양한 작업을 도와준다.
s = "example12test"
sorted_s = ''.join(sorted(s))
print(sorted_s) # 12aeelmpsttx
정렬 후 숫자나 문자의 합 구하기
s = "Python555"
# 숫자와 문자를 구분해 합 구하기
def sum_of_digits_and_characters(s):
digit_sum = sum(int(char) for char in s if char.isdigit())
letter_sum = sum(ord(char) for char in s if char.isalpha())
return digit_sum, letter_sum
digit_sum, letter_sum = sum_of_digits_and_characters(s)
print(f"Digit Sum: {digit_sum}, Letter Sum: {letter_sum}")
# Digit Sum: 15, Letter Sum: 642
가장 많이 사용된 단어 체크
from collections import Counter
s = "example test example example sample test"
# 단어 빈도수 체크
def most_common_word(s):
words = s.split()
counter = Counter(words) # Counter 객체를 생성해 words 리스트의 각 단어 빈도수를 계산
most_common = counter.most_common(1) # 가장 많이 사용된 단어와 그 빈도수를 튜플 형태로 저장
return most_common[0] # 가장 많이 사용된 단어 반환
most_common = most_common_word(s)
print(f"Most Common Word: {most_common[0]}, Count: {most_common[1]}") # Most Common Word: example, Count: 3
문자열 치환하기
문자열 치환은 특정 위치의 단어를 제시된 단어로 변경하는 유형의 문제다.
문자열의 특정 위치에 있는 단어를 찾고 이를 새로운 단어로 바꿀 수 있어야 한다.
파이썬의 문자열 메서드인 replace()
를 사용해 쉽게 해결할 수 있다.
기본적인 문자열 치환
파이썬의 replace()
메서드는 문자열 내에서 특정 부분 문자열을 다른 문자열로 교체할 수 있다.
s = "Hello, World!"
new_s = s.replace("World", "Python")
print(new_s) # Hello, Python!
이 메서드는 원래 문자열을 변경하지 않고 변경된 새로운 문자열을 반환한다.
특정 위치의 단어 치환
단순히 단어를 치환하는 것뿐만 아니라, 특정 위치의 단어를 치환해야 하는 경우도 있다.
예를 들어, 문자열에서 두 번째 단어를 다른 단어로 바꾸는 경우를 생각해 볼 수 있다.
s = "The quick brown fox"
words = s.split()
words[1] = "slow"
new_s = " ".join(words)
print(new_s) # The slow brown fox
references
프로그래머스 코딩 테스트 문제 풀이 전략: 파이썬 편 | 김범수 - 교보문고
프로그래머스 코딩 테스트 문제 풀이 전략: 파이썬 편 | 핵심 개념, 프로그래머스에서 선별한 81개 문제 풀이, PCCP 대비까지! 합격에 한 걸음 더 가까워지는 실전형 코딩 테스트 문제 풀이 가이드
product.kyobobook.co.kr
'CSE > 알고리즘 (algorithm)' 카테고리의 다른 글
[프로그래머스] 2개 이하로 다른 비트 (python) (0) | 2024.06.07 |
---|---|
[프로그래머스] 시저 암호 (python) (0) | 2024.06.06 |
[프로그래머스] 교점에 별 만들기 (python) (1) | 2024.06.06 |
파이썬에서 시간 복잡도 줄이기 (1) | 2024.06.05 |
[알고리즘실습] 2주차 실습과제 (0) | 2023.03.20 |
컴퓨터 전공 관련, 프론트엔드 개발 지식들을 공유합니다. React, Javascript를 다룰 줄 알며 요즘에는 Typescript에도 관심이 생겨 공부하고 있습니다. 서로 소통하면서 프로젝트 하는 것을 즐기며 많은 대외활동으로 개발 능력과 소프트 스킬을 다듬어나가고 있습니다.
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!