문자열 조작하기(re 함수)
문자열 조작하기는 카카오 코테에서 level1,2에 단골 출제입니다.
알고리즘이나 자료구조가 어렵지 않고, 문자열 조작이 필요한 문제가 많이 나오는 것 같습니다.
카카오 문제는 문장이 길기 때문에 개인적으로 문제 이해 시간이 좀 걸려서,
경험상 level1, 2는 30분 안에는 풀어야 하는 것 같습니다.
re 내장함수 : 문자열에서 숫자만 추출
import re
re.sub(pattern, repl, string) # string에서 pattern과 일치하는 문자를 repl로 교체
re.findall(pattern, string) # string에서 pattern에 해당하는 내용을 리스트로 리턴
re.search(pattern, string) # string에서 pattern 추출
re.split(pattern,string)
(1) 모든 숫자들을 문자열로
string = 'aaa1234, ^&*2233pp'
num = re.sub(r'[^0-9]','',string)
print(num) # 12342233 <class 'str'>
(2) 연속된 숫자를 리스트로
string = 'aaa1234, ^&*2233pp'
num = re.findall(r'\d+', string)
print(num) # ['1234', '2233']
string = 'the theme is other'
pattern = r'\bthe\b'
match = re.findall(pattern, string)
print(match) # 출력: ['the']
(3) 패턴을 리스트로
arr = "1S2D*3T"
pattern = '(\d+)([SDT])([*#]?)'
re.findall(pattern, arr) # [('1', 'S', ''), ('2', 'D', '*'), ('3', 'T', '')]
(4) 패턴을 기준으로 잘라 리스트로
s = "img12.png"
parts = re.split('(\d+)', s) # ['img', '12', '.png']
parts = re.split(r'\d+',s) # ['img', '.png']
정규표현식을 사용해서 split 하면 해당 부분을 포함해서 리스트로 return 하고,
raw string으로 만들어주는 r 을 사용하면 해당 부분을 빼고 리스트로 return 한다.
패턴 | 설명 | 예제 | |
---|---|---|---|
^ | 이 패턴으로 시작해야 함 | ^abc : abc로 시작해야 함 (abcd, abc12 등) | |
$ | 이 패턴으로 종료되어야 함 | xyz$ : xyz로 종료되어야 함 (123xyz, strxyz 등) | |
[문자들] | 문자들 중에 하나이어야 함. 가능한 문자들의 집합을 정의함. | [Pp]ython : “Python” 혹은 “python” | |
[^문자들] | [문자들]의 반대로 피해야할 문자들의 집합을 정의함. | [^aeiou] : 소문자 모음이 아닌 문자들 | |
| | 두 패턴 중 하나이어야 함 (OR 기능) | a | b : a 또는 b 이어야 함 | |
? | 앞 패턴이 없거나 하나이어야 함 (Optional 패턴을 정의할 때 사용) | \d? : 숫자가 하나 있거나 없어야 함 | |
+ | 앞 패턴이 하나 이상이어야 함 | \d+ : 숫자가 하나 이상이어야 함 | |
* | 앞 패턴이 0개 이상이어야 함 | \d* : 숫자가 없거나 하나 이상이어야 함 | |
패턴{n} | 앞 패턴이 n번 반복해서 나타나는 경우 | \d{3} : 숫자가 3개 있어야 함 | |
패턴{n, m} | 앞 패턴이 최소 n번, 최대 m 번 반복해서 나타나는 경우 (n 또는 m 은 생략 가능) | \d{3,5} : 숫자가 3개, 4개 혹은 5개 있어야 함 | |
\d | 숫자 0 ~ 9 | \d\d\d : 0 ~ 9 범위의 숫자가 3개를 의미 (123, 000 등) | |
\w | 문자를 의미 | \w\w\w : 문자가 3개를 의미 (xyz, ABC 등) | |
\s | 화이트 스페이스를 의미하는데, [\t\n\r\f] 와 동일 | \s\s : 화이트 스페이스 문자 2개 의미 (\r\n, \t\t 등) | |
. | 뉴라인(\n) 을 제외한 모든 문자를 의미 | .{3} : 문자 3개 (F15, 0x0 등) | ㅊ |
(4) 문장 속에서 숫자 추출
string = "Request 12345 Finished."
pattern = "Request (\d+) Finished."
group = re.search(pattern, string)
print(group[1]) # 12345 <class 'str'>
공백 제거
(1) 모든 공백 제거
string.replace(' ','')
re.sub(r'\s','',string)
(2) 양쪽 공백 제거
s.strip()
(3) 왼쪽 혹은 오른쪽 공백 제거
s.lstrip()
s.rstrip()
문자열 리스트 비교하기
set의 &(교집합), | (합집합), -(차집합)을 통해 구할 수 있습니다.
문자열의 내장함수 이용
문자열의 숫자, 알파벳을 판별할 때 쓰는 함수입니다.
(1) isalpha()
알파벳으로만 구성되었는지 판별 (case insensitive)
s = 'daD+'
d = 'da '
s.isalpha() # False
d.isalpha() # False
(2) isdecimal(), isdigit(), isnumeric()
-
isdecimal ⊆ isdigit ⊆ isnumeric
-
isdecimal( ): 어떤 문자열이 int형으로 변환이 가능하면 True를 반환
-
isdigit( ): 어떤 문자열이 숫자의 형태면 True를 반환 (예: 3², ², ..)
-
isnumeric( ): 숫자값 표현에 해당하는 문자열이면 True를 반환 (예: 3², ², ½, …)
s = '234'
s.isdigit() # Ture
(3) isalnum()
주어진 문자열이 알파벳+숫자로만 구성되었는지 판별
s = 'se12'
s.isalnum() # True
정렬
1. 숫자로 변환하여 정렬
a = ['2', '01', '1', '10']
a_sorted = sorted(a) # ['01', '1', '10', '2']
# 문자열을 숫자로 변환하여 정렬
a_sorted = sorted(a, key=int) # ['1', '01', '2', '10']
2. 2차배열의 기준 열을 기준으로 정렬
# 2차원 배열
arr = [['Apple', 'banana'], ['Cherry', 'apple'], ['Date', 'cherry'], ['banana', 'Date']]
# 각 행의 1열을 소문자로 변환한 후 그 결과를 기준으로 정렬
arr_sorted = sorted(arr, key=lambda x: x[1].lower())
# [['Cherry', 'apple'], ['Apple', 'banana'], ['Date', 'cherry'], ['banana', 'Date']]
코테 문제
1. 튜플 (level_2)
-
문제 설명 : 문제의 input인 s가 표현하는 튜플을 배열에 담아 return
-
풀이 : re.findall 함수로 연속된 숫자를 list로 받았습니다.
import re
def solution(s):
dic = {}
n = 1
result = []
s = s.split('},')
for ss in s:
ss = re.findall(r'\d+',ss)
dic[len(ss)] = set(ss)
n = max(n,len(ss))
if len(ss) == 1:
result.append(int(ss.pop()))
for i in range(2,n+1):
intersection = dic[i] - dic[i-1]
result.append(int(intersection.pop()))
return result
2. 다트 게임 (level_1)
-
문제 상황 : 3번의 다트 게임을 진행하는데, 보너스(S, D, T)와 옵션(*, #)을 고려하여 점수 합계를 출력합니다.
-
풀이 : re.findall 함수로 pattern을 list로 받았습니다.
import re
def solution(dartResult):
answer = []
bonus_dic = {'S':1, 'D':2, 'T':3}
pattern = '(\d+)([SDT])([*#]?)'
dart = re.findall(pattern, dartResult)
for i in range(3):
answer.append(int(dart[i][0])**bonus_dic[dart[i][1]])
if dart[i][2] == '*':
answer[i] = answer[i]*2
if i != 0:
answer[i-1] = answer[i-1]*2
elif dart[i][2] == '#':
answer[i] = -answer[i]
return sum(answer)
- 모법 풀이 : option_point 까지 하나의 식으로 고려
import re
def solution(dartResult):
bonus = {'S' : 1, 'D' : 2, 'T' : 3}
option = {'' : 1, '*' : 2, '#' : -1}
p = re.compile('(\d+)([SDT])([*#]?)')
dart = p.findall(dartResult)
for i in range(len(dart)):
if dart[i][2] == '*' and i > 0:
dart[i-1] *= 2
dart[i] = int(dart[i][0]) ** bonus[dart[i][1]] * option[dart[i][2]]
answer = sum(dart)
return answer
3. 뉴스 클러스터링 (level_2)
-
문제 상황 : 자카드 유사도 방법을 통해 두 기사 제목의 유사도를 판정하는 문제입니다. 각 기사 제목에서 두글자씩 다중집합의 원소로 생각하고, 두 다중집합간의 교집합 / 합집합 * 65536 값이 answer입니다.
-
문제 접근 : collections.Count 함수를 통해 기사제목 글자가 중복되는 숫자를 dic로 정리합니다. 두 기사의 dic.keys()값을 합쳐서 교집합은 min(dic1[key], dic2[key])로 하고, 합집합은 두 기사의 리스트 길이를 합치고 교집합개수를 뺀 값으로 합니다.
-
제한 사항 : 2 <= str1, str2 <= 1000
from collections import Counter
import math
import re
def solution(str1, str2):
answer = 0
n = len(str1)
m = len(str2)
input1 = []
input2 = []
input1 = [str1[i:i+2].casefold() for i in range(n-1) if str1[i:i+2].isalpha()] #if not re.findall('[^a-zA-Z]+', str1[i:i+2])]
dic1 = dict(Counter(input1))
input2 = [str2[i:i+2].casefold() for i in range(m-1) if str2[i:i+2].isalpha()]
dic2 = dict(Counter(input2))
# 예외처리
if not input1 and not input2:
return 65536
keys_set = set(list(dic1.keys()) + list(dic2.keys()))
intersectN = 0
for key in keys_set:
if key in dic1 and key in dic2:
intersectN += min(dic1[key], dic2[key])
unionN = len(input1) + len(input2) - intersectN
answer = intersectN / unionN
return math.trunc(answer*65536)
str1 = "e=m*c^2"
str2 = "E=M*C^2"
print(solution(str1, str2))
Leave a comment